如何在 eloquent 的模型上创建条件? (Laravel 5.3)

How to create the conditions on the model of eloquent? (Laravel 5.3)

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

<?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';

我该怎么做?

更新:

the original article refers my personal blog

软删除特征

在laravel中,我们通过扩展Illuminate\Database\Eloquent\Model定义了自己的模型。要软删除模型实例,我们应该在模型中使用 Illuminate\Database\Eloquent\SoftDeletes 特征。 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()函数进行删除操作。一定要注意!

这里我们应该知道:

An inherited member from a base class is overridden by a member inserted by a Trait. The precedence order is that members from the current class override Trait methods, which in turn override inherited methods.

所以当 performDeleteOnModel() 调用时执行的确切函数是 SoftDeletes 特征中具有相同名称的函数,而不是 Model class 中的函数。现在我们回到特征:

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

    return $this->runSoftDelete();
}

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

获取问题

提问者想在删除时使用不同的DELETED_AT列。仅通过覆盖 getDeletedAtColumn() 来保持软删除机制的良好运行还有很多不足之处。为什么被删除的模型被软删除了,结果还在结果中?

Model class 被构造时,它将通过调用它们的 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 特质。

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

这里的 trait 通过调用 static::addGlobalScope() 注册了一个 SoftDeletingScope class 有一个 apply() 方法。位于Model class 的方法将其存储到$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 class:

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

    $this->extend($builder);
}

它将对 select 那些 DELETED_AT 列为空的记录的每个查询添加约束。而这就是SoftDeletes的秘密。

动态DELETED_AT列

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

为了解决提问者的动态DELETED_AT栏目问题,你需要实现你自己的SoftDeletingScope class这样的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赋值

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

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

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

 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();
}