繁体   English   中英

Laravel,简化关系:多种可排序模型

[英]Laravel, simplifying relationships: multiple sortable models

在进行Laravel项目时,我注意到模型中的重复模式:

  • Model A Model B
  • Model B可以选择隐藏
  • Model B可以选择分类

例子:

画廊有图片。 您可以选择不显示图片,也可以选择对图片进行排序。

“查看我们的团队”页面。 您可以选择不显示某些员工,也可以选择对他们排序。

主页上的滑块。 您可以选择不显示某些图像,也可以选择对它们进行排序。

我一直在实现所有这些,如下所示:

class ModelA extends Model {
    function modelBs() {
        $this->hasMany('modelB');
    }
}

class modelB extends Model {
    protected $fillable = ['visible', 'order'];
    function modelA() {
        $this->belongsTo('modelA');
    }
}

我还将反复重新实现(复制/粘贴)用于在刀片模板中显示这些代码的代码:

@foreach( $modelA->modelBs()->sortBy('order') as $modelB )
    @if( $modelB->visible )
        <li>{{ $modelB->output() }}</li>
    @endif
@endforeach

在管理面板中,我必须反复重新实现(复制/粘贴)jQuery-UI可排序小部件,以修改订单,序列化您的决定并将其提交给服务器,然后保存此订单(通过更新每个Model B '适当订购)

它一发不可收拾,我想起了《拉拉卡斯特》的格言:

如果您发现自己使用的是复制粘贴,那么可能会有更好的方法

当我试图考虑一个更好的解决方案时,这是我想象中的第一个关系:

  • Model A SortThing
  • SortThing morphsTo sortable
  • Model B具有sortable

这样,我知道可以对任何 SortThing进行排序或隐藏,并且SortThing可以引用任何可排序的对象(图片,员工,滑块面板等)

问题是这样做并不能使我的代码变得更干燥:

class ModelA extends Model {
    function modelBs() {
        $this->hasMany('SortThing');
    }
}

class modelB extends Model {
    function sortable() {
        $this->morphsOne('SortThing', 'sortable');
    }
}

class SortThing extends Model {
    protected $fillable = ['visible', 'order'];
    function sortable() {
        $this->morphTo();
    }
}

@foreach( $modelA->modelBs()->sortBy('order') as $modelB )
    @if( $modelB->visible )
        <li>{{ $modelB->sortable->output() }}</li>
    @endif
@endforeach

我添加了一个额外的类,并在输出中需要sortable-> ,但我仍在复制/粘贴代码。

关于如何清理我的代码的任何建议将不胜感激。 还是有点Laravel新手。

如果所得到的关系不需要我在对对象重新排序时更新18个数据库行,则可以得到加分,因为列表很长,这可能会导致一些丑陋的开销。

更新

尝试下面的@gpopoteur答案(修复了他在renderItems函数的声明中的renderItems )后,出现以下错误:

[2015-03-24 13:08:52] production.ERROR: exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'App\Slider and App\HasSortableItemsTrait define the same property ($sortItemClass) in the composition of App\Slider. However, the definition differs and is considered incompatible. Class was composed' in /var/www/example.com/app/Slider.php:26

Slider.php看起来像这样:

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Slider extends Model {

    use HasSortableItemsTrait;

    protected $sortItemClass = 'App\SliderPanel';

    //
    public function sliderable() {
        return $this->morphTo();
    }

    public function panels() {
        return $this->hasMany('App\SliderPanel');
    }

    public function output() {
        $panels = $this->panels();
        return "Test?";

    }

} // Line 26

和HasSortableItemsTrait.php看起来像这样:

<?php namespace App;

trait HasSortableItemsTrait {

    protected $sortItemClass; // Also tried $sortItemClass = ''; and $sortItemClass = null;

    public function items() {
        $this->hasMany($this->sortItemClass)->sortyBy('order')->where('visible', '=', true);
    }

    public function renderItems($htmlTag = '<li>:item</li>') {
        $render = '';
        foreach( $this->items() as $item ){
            $render .= str_replace($item->render(), ':item', $htmlTag);
        }

        return $render;
    }

}

更新2

我发现注释掉以下行可以解决我的问题:

protected $sortItemClass;

当然,我仍然必须确保使用该特征的任何东西都定义了$sortItemClass否则在调用items()时它将失败

现在我收到一个新错误:

[2015-03-24 13:34:50] production.ERROR: exception 'BadMethodCallException' with message 'Call to undefined method Illuminate\Database\Query\Builder::sortBy()' in /var/www/example.com/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php:1992

我仔细检查了Laravel文档,我很像90%的人确信sortBy应该是查询生成器上的有效方法...

您可以尝试将所有内容添加到特征中,然后仅use SortableTrait; 在您的模型上。 然后将该类设置为它具有许多其他Sortable项的protected属性。

以下代码未经测试!!!

让我们为具有可排序项目的类定义一个特征。

    trait HasSortableItemsTrait {

        // define this in your class
        // protected $sortItemClass

        public function items() {
            $this->hasMany($sortItemClass)->orderBy('order');
        }

        public function renderItems($htmlTag = '<li>:item</li>']) {
            return $this->items->map(function($item) use ($htmlTag) {
                if( $item->visible ){
                    return str_replace($item->render(), ':item', $htmlTag);
                }
            });
        }

    }

在可排序项目上使用的另一个特征。

    trait IsSortableTrait {

        // define this in your class
        // protected $sortItemParent

        function items() {
            $this->belongsTo($sortItemParent);
        }

        function render(){
            return $this->output();
        }

    }

让我们以具有可排序照片的图库为例。 这是App\\Gallery外观:

    class Gallery extends Model {
        use HasSortableItemsTrait;

        protected $sortItemClass = 'App\Photo';
    }

这就是App\\Photo类的样子:

    class Photo extends Model {
        use IsSortableTrait;

        protected $sortItemParent = 'App\Gallery';
    }

然后,您只需要获取具有许多可排序项目的Item(在本例中为图库)即可。

    $gallery = Gallery::find(1);

在视图中,您只需要调用视图的renderItems()方法即可。

    {{ $gallery->renderItems() }}

我使renderItems方法能够接收如何包装$item->render()将作为输出给出的内容。 例如,在<p></p>您只需要调用如下方法:

    {{ $gallery->renderItems('<p>:item</p>') }}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM