在变形关系中检索父类

时间:2015-11-04 18:38:43

标签: laravel eloquent laravel-5.1

我有这段代码

//ImageableTrait
trait ImageableTrait
{
    public function images()
    {
        return $this->morphMany(Image::class, 'imageable')
            ->orderBy('order', 'ASC');
    }
}

//User
class User extend Model
{
    use ImageableTrait;
}

//Post
class Post extend Model
{
    use ImageableTrait;
}

class ImageCollection extends Collection
{
    public function firstOrDefault()
    {
        if ($this->count() === 0) {
            $image = new Image();
            $image->id = 'default';
            $image->imageable_type = '/* I need the parent className here */';
            $image->imageable_id = '.';
        }

        return $this->first();
    }
}

//Image
class Image extend Model
{
    public function imageable
    {
        return $this->morphTo();
    }

    public function newCollection(array $models = [])
    {
        return new ImageCollection($models);
    }

    public function path($size)
    {
        //Here, there is some logic to build the image path and it needs
        //the imageable_type attribute no matter if there is
        //an image record in the database or not
        return;
    }
}

我希望能够这样做

$path = User::find($id)->images->firstOrDefault()->path('large');

但是我无法弄清楚如何让父类名正确地构建路径......

我尝试使用$ morphClass或getMorphClass(),但无法弄清楚如何正确使用它,或者它是否是正确的方法。

对此有何看法?

2 个答案:

答案 0 :(得分:1)

我认为你可以保持简单并删除ImageCollection类,因为已经有firstOrNew方法似乎是你正在寻找的方法。

firstOrNew方法接受您要匹配的属性数组。如果您不关心属性,则可以传递一个空数组。如果数据库中没有匹配项,它将使用正确的父类型创建一个新实例。

$path = User::find($id)->images()->firstOrNew([])->path('large');

注意:我调用images()方法来获取MorphMany对象,以便我可以调用firstOrNew方法。换句话说,您需要添加括号。否则,您会收到Collection

编辑:如果你想通过自动设置一些默认属性来简化一些事情,你可以将它添加到你的ImageableTrait:

public function imagesOrDefault()
{
    $defaultAttributes = ['id' => 'default'];
    return $this->images()->firstOrNew($defaultAttributes);
}

然后,您可以执行以下操作:$path = User::find($id)->imagesOrDefault()->path('large');

请注意,您的默认属性必须是可填写的才能使其生效。此外,imageable_id和imageable_type将自动设置为您父母的ID和类型。

如果你想将imageable_id的默认值设置为一个句点而不是父的id,那么你必须稍微改一下它,它看起来很像你的原始代码,除非它会进入你的ImageableTrait。

public function imagesOrDefault()
{
    // First only gets one image.
    // If you want to get all images, then change it to get.
    // But if you do that, change the conditional check to a count.
    $image = $this->images()->first();

    if (is_null($image))
    {
        $image = new Image();
        $image->id = 'default';
        $image->imageable_type = $this->getMorphClass();
        $image->imageable_id = '.';
    }
    return $image;
}

答案 1 :(得分:0)

好的家伙我发现现在看起来效果很好的东西,所以我会坚持下去。

在图像模型中,我在制作新集合时添加了一些代码:

public function newCollection(array $models = [])
{
    $morphClass = '';

    $parent = debug_backtrace(false, 2)[1];

    if (isset($parent['function']) AND $parent['function'] === 'initRelation') {
        if (isset($parent['args'][0][0])) {
            $morphClass = get_class($parent['args'][0][0]);
        }
    }
    return new ImageCollection($models, $morphClass);
}

然后,我只需通过ImageCollection

的构造函数检索morphClass
private $morphClass;

public function __construct($items = [], $morphClass)
{
    parent::__construct($items);
    $this->morphClass = $morphClass;
}

public function firstOrDefault()
{
    if ($this->count() === 0) {
        $image = new Image();
        $image->id = 'default';
        $image->imageable_type = $this->morphClass;
        $image->imageable_id = '.';
    }

    return $this->first();
}

这样,我可以简单地调用这个方法

User::with('images')->get()->images->firstOrDefault()

在许多情况下,这似乎工作得非常好,如果我有时会遇到一些问题,我会更新这篇文章。