【问题标题】:How to create the conditions on the model of eloquent? (Laravel 5.3)如何为雄辩的模式创造条件? (Laravel 5.3)
【发布时间】:2017-08-03 01:23:50
【问题描述】:

我在控制器上的删除方法是这样的:

<?php
public function deleteMessage($id, $seller_id, $buyer_id)
{
    //if the seller delete
    if($seller_id == auth()->user->id)
        //const DELETED_AT = 'deleted_by_seller';
    //if the buyer delete
    else($buyer_id == auth()->user->id)
        //const DELETED_AT = 'deleted_by_buyer';

    $result = Message::destroy($id);
    return $result;
}

我的模型是这样的:

<?php
namespace App\Models;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent; 
use Jenssegers\Mongodb\Eloquent\HybridRelations;
use Jenssegers\Mongodb\Eloquent\SoftDeletes;
class MessageThread extends Eloquent
{
    use HybridRelations; 
    use SoftDeletes; 
    // const DELETED_AT = 'deleted_by_seller';
    // const DELETED_AT = 'deleted_by_buyer';
    protected $connection = 'mongodb';
    protected $dates = ['deleted_by_seller', 'deleted_by_buyer'];
    protected  $fillable = ['subject', 'information', 'created_at', 'update_at'];
}

我想喜欢这个:

如果卖家删除消息则:const DELETED_AT = 'deleted_by_seller';

如果买家删除消息则:const DELETED_AT = 'deleted_by_buyer';

我该怎么做?

【问题讨论】:

    标签: php mongodb laravel laravel-5.3 laravel-eloquent


    【解决方案1】:

    更新:

    原文参考我的personal blog

    SoftDeletes 特征

    在 laravel 中,我们通过扩展 Illuminate\Database\Eloquent\Model 来定义自己的模型。要软删除模型实例,我们应该在模型中使用Illuminate\Database\Eloquent\SoftDeletes trait。 runSoftDelete()SoftDeletes trait 构建sql查询的关键函数,获取用于标记记录是否已被删除的列,然后使用当前时间戳更新该列。

    protected function runSoftDelete()
    {
        $query = $this->newQueryWithoutScopes()->where($this->getKeyName(), $this->getKey());
        $this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
        $query->update([$this->getDeletedAtColumn() => $this->fromDateTime($time)]);
    }
    

    Delete() 的过程

    当我们在模型上调用delete() 函数时会发生什么?

    由于我们自己的模型扩展了Illuminate\Database\Eloquent\Model,我们来看看它。这是delete() 函数:

    public function delete()
    {
        if (is_null($this->getKeyName())) {
            throw new Exception('No primary key defined on model.');
        }
    
        if ($this->exists) {
            if ($this->fireModelEvent('deleting') === false) {
                return false;
            }
    
            // Here, we'll touch the owning models, verifying these timestamps get updated
            // for the models. This will allow any caching to get broken on the parents
            // by the timestamp. Then we will go ahead and delete the model instance.
            $this->touchOwners();
    
            $this->performDeleteOnModel();
    
            $this->exists = false;
    
            // Once the model has been deleted, we will fire off the deleted event so that
            // the developers may hook into post-delete operations. We will then return
            // a boolean true as the delete is presumably successful on the database.
            $this->fireModelEvent('deleted', false);
    
            return true;
        }
    }
    

    代码很清楚。它确保模型具有primaryKey,并且实例首先存在于数据库中。然后调用performDeleteOnModel()函数进行删除操作。一定要注意!

    这里我们应该知道:

    从基类继承的成员被由 Trait 插入的成员覆盖。优先顺序是当前类中的成员覆盖 Trait 方法,而 Trait 方法又会覆盖继承的方法。

    因此,当调用performDeleteOnModel()SoftDeletes trait 中的同名函数而不是Model 类中的函数时,执行的确切函数。现在我们回到 trait:

    protected function performDeleteOnModel()
    {
        if ($this->forceDeleting) {
            return $this->newQueryWithoutScopes()->where($this->getKeyName(), $this->getKey())->forceDelete();
        }
    
        return $this->runSoftDelete();
    }
    

    嗯,它调用runSoftDelete(),我们一开始就谈到了。这就是软检测的过程。

    获取问题

    提问者希望在删除时使用不同的DELETED_AT 列。仅通过覆盖getDeletedAtColumn() 来保持软删除机制正常工作还有很多不足之处。为什么被软删除的模型仍然在结果中?

    Model 类被构造时,它将通过调用他们的boot[TraitName] 方法来引导特征。因此这里是bootSoftDelete() 方法。

    protected static function bootTraits()
    {
        foreach (class_uses_recursive(get_called_class()) as $trait) {
            if (method_exists(get_called_class(), $method = 'boot'.class_basename($trait))) {
                forward_static_call([get_called_class(), $method]);
            }
        }
    }
    

    现在让我们再次关注SoftDeletes trait。

    public static function bootSoftDeletes()
    {
        static::addGlobalScope(new SoftDeletingScope);
    }
    

    这里的 trait 通过调用 static::addGlobalScope() 注册了一个具有 apply() 方法的 SoftDeletingScope 类。位于Model 类中的方法将其存储到$globalScopes 数组中。

    public static function addGlobalScope(ScopeInterface $scope)
    {
        static::$globalScopes[get_called_class()][get_class($scope)] = $scope;
    }
    

    在模型上构建查询时,会自动调用applyGlobalScopes()方法,逐一访问$globalScopes数组中的实例并调用它们的apply()方法。

    public function applyGlobalScopes($builder)
    {
        foreach ($this->getGlobalScopes() as $scope) {
            $scope->apply($builder, $this);
        }
    
        return $builder;
    }
    

    我们现在将揭开问题的面纱。在SoftDeletingScope类中:

    public function apply(Builder $builder, Model $model)
    {
        $builder->whereNull($model->getQualifiedDeletedAtColumn());
    
        $this->extend($builder);
    }
    

    它将在每个查询上添加一个约束,以选择那些DELETED_AT 列为空的记录。这就是SoftDeletes 的秘密。

    动态 DELETED_AT 列

    首先,我需要重申,我不推荐这种使用动态DELETED_AT 列的行为。

    为了解决提问者动态DELETED_AT列的问题,你需要实现自己的SoftDeletingScope类,有这样的apply()函数:

    public function apply(Builder $builder, Model $model)
    {
        $builder->where(function ($query){
            $query->where('DELETED_AT_COLUMN_1',null)->orWhere('DELETED_AT_COLUMN_2',null);
        });
    
        $this->extend($builder);
    }
    

    然后用它覆盖bootSoftDeletes()

    public static function bootSoftDeletes()
    {
        static::addGlobalScope(new YourOwnSoftDeletingScope);
    }
    

    原答案:

    您无法在运行时更改const 变量的值。所以需要手动赋值CREATED_AT

    【讨论】:

    • 这是否意味着我的情况没有解决方案?因为我需要根据某些条件来改变它
    • 如果你坚持这样。您可以覆盖模型中的 getDeletedAtColumn 方法。每次销毁模型,SoftDeletes 都会调用该方法获取需要修改的列。
    • 我还是一头雾水。你最好更新你的答案更详细
    • 是的,@Paras 的代码表达了我的主要思想。你可以把它作为参考。 :)
    • getDeletedAtColumn 在什么时间使用。 @Paras 描述的代码,未使用的
    【解决方案2】:

    首先,我认为这不是您拥有的理想数据库结构。您应该有两列:deleted_by 和 deleted_at,而不是包括 deleted_by_seller 和 deleted_by_buyer,其中一列始终为空。

    如果您仍想继续使用现有的数据库结构,正如@William 指出的那样,试试这个:

    在您的模型类中,添加以下内容:

     protected $deletedAtCol = "deleted_at";
    
     /**
     * Get the name of the "deleted at" column.
     *
     * @return string
     */
    public function getDeletedAtColumn()
    {
        return $this->deletedAtCol;
    }
    
    /**
     * Set the name of the "deleted at" column.
     * @param string $colName
     * @return string
     */
    public function setDeletedAtColumn($colName)
    {
        $this->deletedAtCol = $colName;
    }
    

    然后在你的控制器中,添加这个:

    public function deleteMessage($id, $seller_id, $buyer_id)
    {
        $message = Message::findOrFail($id); 
        //if the seller delete
        if($seller_id == auth()->user->id)
            $message->setDeletedAtCol("deleted_by_seller");
        //if the buyer delete
        else($buyer_id == auth()->user->id)
            $message->setDeletedAtCol("deleted_by_buyer");
    
        return $message->delete();
    }
    

    【讨论】:

    • 如果我使用你的方式,那就是使用两列:deleted_by 和 deleted_at。如何填写deleted_by?如果deleted_at则已经自动填充了
    • 对于该方法,您可以使用模型事件:laravel.com/docs/5.4/eloquent#events
    • 只需为消息模型添加一个deleting Eloquent 事件并添加类似$message-&gt;deleted_by = auth()-&gt;user()-&gt;id的内容
    • 我试试这个:$message = message::findOrFail($id); $message-&gt;deleted_by = auth()-&gt;user()-&gt;id; return $message-&gt;delete();。它不起作用
    • 在删除前尝试保存邮件
    猜你喜欢
    • 1970-01-01
    • 2017-05-19
    • 2017-04-07
    • 2021-12-18
    • 2017-04-28
    • 2017-02-16
    • 2016-05-14
    • 2015-08-08
    • 2017-07-01
    相关资源
    最近更新 更多