【问题标题】:If relationship doesn't exist use parent's relationship如果关系不存在,则使用父母的关系
【发布时间】:2020-01-17 22:02:26
【问题描述】:

在我的应用程序中,我有游戏和锦标赛,游戏可以选择属于锦标赛。 Game 和 Tournament 都可以与 PhotoAlbum 建立 belongsTo 关系。

我现在想要的是,如果游戏没有 PhotoAlbum,它将尝试返回它的 Tournament 的 PhotoAlbum(如果这两个东西存在)。如果我使用withCount 进行查询,我也希望它采用这条路线。

我尝试将支票添加到游戏的专辑关系中,但这似乎不起作用:

class Game extends Model {

    public function album()
    {
        if ($this->album_id == null && $this->tournament_id != null ) {
            return $this->tournament->album();
        }

        return $this->belongsTo('App\Models\PhotoAlbum');
    }
}

需要在查询构建器中使用withCount(),而不会导致我不得不在整个现有代码中编写一大堆新的 if/then 检查。

--

不太关心withCount 的工作,因为我可以制作一个适用于album_count 的自定义属性getter。

【问题讨论】:

标签: laravel eloquent


【解决方案1】:

我讨厌成为回答自己问题的人,但我讨厌成为不分享更有效的方法的人。

我最终创建了一个在数据级别执行连接和检查的视图。这使它成为一个简单的joinifnull,然后更改我的模型以使用该表而不是基础。这非常适合读取和查询,但显然不适用于 CRUD 操作。

由于此视图不可更新,我最终在准备进行任何写入时利用模型事件系统将模型切换到基表,然后在完成后返回。我选择了观察者模式,因为它让一切都保持整洁。

观察者:

<?php
namespace App\Models\Observers;

use App\Models\Contracts\IPersistTo;

/**
 * Class PersistToObserver
 *
 *
 * @package App\Models\Observers
 */
class PersistToObserver
{
    protected function useReadTable(IPersistTo $model)
    {
        $model->setTable($model->getReadTable());
    }

    protected function useWriteTable(IPersistTo $model)
    {
        $model->setTable($model->getWriteTable());
    }

    /**
     * Switch the model to use the write table before it goes to the DB
     * @param IPersistTo $model
     */
    public function creating(IPersistTo $model)
    {
        $this->useWriteTable($model);
    }

    /**
     * Switch the model to use the write table before it goes to the DB
     * @param IPersistTo $model
     */
    public function updating(IPersistTo $model)
    {
        $this->useWriteTable($model);
    }

    /**
     * Switch the model to use the write table before it goes to the DB
     * @param IPersistTo $model
     */
    public function saving(IPersistTo $model)
    {
        $this->useWriteTable($model);
    }

    /**
     * Switch the model to use the write table before it goes to the DB
     * @param IPersistTo $model
     */
    public function deleting(IPersistTo $model)
    {
        $this->useWriteTable($model);
    }

    /**
     * Switch the model to use the write table before it goes to the DB
     * @param IPersistTo $model
     */
    public function restoring(IPersistTo $model)
    {
        $this->useWriteTable($model);
    }


    /**
     * Model has been written to the BD, switch back to the read table
     * @param IPersistTo $model
     */
    public function created(IPersistTo $model)
    {
        $this->useReadTable($model);
    }

    /**
     * Model has been written to the BD, switch back to the read table
     * @param IPersistTo $model
     */
    public function updated(IPersistTo $model)
    {
        $this->useReadTable($model);
    }

    /**
     * Model has been written to the BD, switch back to the read table
     * @param IPersistTo $model
     */
    public function saved(IPersistTo $model)
    {
        $this->useReadTable($model);
    }

    /**
     * Model has been written to the BD, switch back to the read table
     * @param IPersistTo $model
     */
    public function deleted(IPersistTo $model)
    {
        $this->useReadTable($model);
    }

    /**
     * Model has been written to the BD, switch back to the read table
     * @param IPersistTo $model
     */
    public function restored(IPersistTo $model)
    {
        $this->useReadTable($model);
    }

}

IPersistTo 合约/接口

<?php
namespace App\Models\Contracts;

interface IPersistTo
{

    /**
     * @return string - the name of the table to read from (should be the same as the default $table)
     */
    public function getReadTable();

    /**
     *  @return string - the name of the table to write to
     */
    public function getWriteTable();

    /**
     * Set the table associated with the model. Fulfilled by Model.
     *
     * @param  string  $table
     * @return $this
     */
    public function setTable($table);

}

我的模型中的设置(被截断为相关信息)

class Game extends Model implements IPersistTo {

    protected $table = 'game_with_album_fallback';

    /**
     * @return string - the name of the table to read from (should be the same as the default $table)
     */
    public function getReadTable()
    {
        return 'game_with_album_fallback';
    }

    /**
     * @return string - the name of the table to write to
     */
    public function getWriteTable()
    {
        return 'games';
    }

}

最后将所有这些连接在一起,将以下行添加到 AppServiceProvider 的启动方法中:

Game::Observe(PersistToObserver::class);

【讨论】:

    【解决方案2】:

    您尝试过HasOneThrough 关系吗?

    public function tournamentAlbum(): HasOneThrough
    {
        return $this->hasOneThrough(Album::class, Tournament::class);
    }
    

    我想没有简单的方法可以增加当前关系并防止重构。这是因为使用空模型实例部分解决了急切加载。

    【讨论】:

    • 我实际上忘记了 hasOneThrough 的存在,但是,由于急切的负载,这里没有运气
    【解决方案3】:

    你可以在下面这样做

    $game = Game::find(1);
    
    if(! isset($game->album)) { // you can different if's here to check if not album exist
       return $game->tournament->album;
    } 
    

    【讨论】:

    • 不适用于 withCount 查询,我不想在整个应用程序中编写 50 个 if 语句
    猜你喜欢
    • 2020-12-25
    • 1970-01-01
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2017-07-31
    相关资源
    最近更新 更多