Laravel 13: Updated ordered relations with delete modifier

Allows deletes to work as before, ignoring the ordering on delete which
would otherwise cause an error on mysql.
This commit is contained in:
Dan Brown
2026-10-11 17:20:30 +01:00
parent 93e8851aae
commit 081069cb73
7 changed files with 45 additions and 16 deletions
+23 -3
View File
@@ -2,18 +2,38 @@
namespace BookStack\App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model as EloquentModel;
use Illuminate\Database\Eloquent\Relations\Relation;
class Model extends EloquentModel
{
/**
* Provides public access to get the raw attribute value from the model.
* Used in areas where no mutations are required, but performance is critical.
*
* @return mixed
*/
public function getRawAttribute(string $key)
public function getRawAttribute(string $key): mixed
{
return parent::getAttributeFromArray($key);
}
/**
* Modify an ordered relation to be deletable across all database types.
* This is used to ensure that delete calls don't use the order, and therefore
* follow the pre-Laravel 13 logic which was MySQL compatible.
*
* This only considers standard delete actions, not force deletes.
*
* @template T of Relation
* @param T $relation
* @return T
*/
public function deletableOrderedRelation(Relation $relation): Relation
{
$relation->onDelete(function (Builder $builder) {
$builder->toBase()->reorder()->delete();
});
return $relation;
}
}
+2 -1
View File
@@ -28,10 +28,11 @@ class Bookshelf extends Entity implements HasDescriptionInterface, HasCoverInter
*/
public function books(): BelongsToMany
{
return $this->belongsToMany(Book::class, 'bookshelves_books', 'bookshelf_id', 'book_id')
$relation = $this->belongsToMany(Book::class, 'bookshelves_books', 'bookshelf_id', 'book_id')
->select(['entities.*', 'entity_container_data.*'])
->withPivot('order')
->orderBy('order', 'asc');
return $this->deletableOrderedRelation($relation);
}
/**
+2 -1
View File
@@ -29,7 +29,8 @@ class Chapter extends BookChild implements HasDescriptionInterface, HasDefaultTe
*/
public function pages(string $dir = 'ASC'): HasMany
{
return $this->hasMany(Page::class)->orderBy('priority', $dir);
$relation = $this->hasMany(Page::class)->orderBy('priority', $dir);
return $this->deletableOrderedRelation($relation);
}
/**
+6 -4
View File
@@ -213,8 +213,9 @@ abstract class Entity extends Model implements
*/
public function activity(): MorphMany
{
return $this->morphMany(Activity::class, 'loggable')
$relation = $this->morphMany(Activity::class, 'loggable')
->orderBy('created_at', 'desc');
return $this->deletableOrderedRelation($relation);
}
/**
@@ -230,8 +231,9 @@ abstract class Entity extends Model implements
*/
public function tags(): MorphMany
{
return $this->morphMany(Tag::class, 'entity')
$relation = $this->morphMany(Tag::class, 'entity')
->orderBy('order', 'asc');
return $this->deletableOrderedRelation($relation);
}
/**
@@ -241,8 +243,8 @@ abstract class Entity extends Model implements
public function comments(bool $orderByCreated = true): MorphMany
{
$query = $this->morphMany(Comment::class, 'commentable');
return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
$query = $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
return $this->deletableOrderedRelation($query);
}
/**
+7 -3
View File
@@ -74,10 +74,11 @@ class Page extends BookChild
*/
public function revisions(): HasMany
{
return $this->allRevisions()
$relation = $this->allRevisions()
->where('type', '=', 'version')
->orderBy('created_at', 'desc')
->orderBy('id', 'desc');
return $this->deletableOrderedRelation($relation);
}
/**
@@ -85,10 +86,11 @@ class Page extends BookChild
*/
public function currentRevision(): HasOne
{
return $this->hasOne(PageRevision::class)
$relation = $this->hasOne(PageRevision::class)
->where('type', '=', 'version')
->orderBy('created_at', 'desc')
->orderBy('id', 'desc');
return $this->deletableOrderedRelation($relation);
}
/**
@@ -105,7 +107,9 @@ class Page extends BookChild
*/
public function attachments(): HasMany
{
return $this->hasMany(Attachment::class, 'uploaded_to')->orderBy('order', 'asc');
$relation = $this->hasMany(Attachment::class, 'uploaded_to')
->orderBy('order', 'asc');
return $this->deletableOrderedRelation($relation);
}
/**
+1 -1
View File
@@ -18,7 +18,7 @@ class RevisionRepo
*/
public function deleteDraftsForCurrentUser(Page $page): void
{
$this->queries->latestCurrentUserDraftsForPageId($page->id)->delete();
$this->queries->latestCurrentUserDraftsForPageId($page->id)->reorder()->delete();
}
/**
+4 -3
View File
@@ -40,7 +40,8 @@ class Role extends Model implements Loggable
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->orderBy('name', 'asc');
$relation = $this->belongsToMany(User::class)->orderBy('name', 'asc');
return $this->deletableOrderedRelation($relation);
}
/**
@@ -87,7 +88,7 @@ class Role extends Model implements Loggable
/**
* Add a permission to this role.
*/
public function attachPermission(RolePermission $permission)
public function attachPermission(RolePermission $permission): void
{
$this->permissions()->attach($permission->id);
}
@@ -95,7 +96,7 @@ class Role extends Model implements Loggable
/**
* Detach a single permission from this role.
*/
public function detachPermission(RolePermission $permission)
public function detachPermission(RolePermission $permission): void
{
$this->permissions()->detach([$permission->id]);
}