【问题标题】:Laravel casts with relationLaravel 强制转换关系
【发布时间】:2017-01-10 10:01:09
【问题描述】:

我正在制作一个调查网站,用户提供从 0 到 10 的答案。 我正在尝试使用 casts 将所有问题和答案存储在 1 行中。

这是分数字段在数据库中的样子

{"5":8,"6":8}

这符合预期。 “5”和“6”是问题的ID,8是用户提供的分数。

我想知道是否可以查询答案但包含与问题的关系。我想看到如下结果

{"5" => {"question: ...", "answer: ..."}

所以我的问题是:是否可以转换为数组但在 Laravel 中包含关系?或者我需要循环数组并获取每一行的问题数据。

谢谢!

【问题讨论】:

  • 我相信这是不可能的。我认为你应该遍历你的数组并使用Question::find($id)

标签: php arrays laravel


【解决方案1】:

您可以使用自定义访问器和修改器做任何您想做的事情,但我不建议将关系存储在数据库中。您不希望在其中引用可能独立更改的整个对象,因此请将其保留为 ids 并在从数据库中获取分数后装饰您的输出。

public function getScoresAttribute($value)
{
    // if using casts to array, this should already be done
    $scores = json_decode($value, true);

    $questions = Question::whereIn('id', array_keys($scores))->get();
    $rs = [];

    $questions->each(function($q) use ($rs, $scores) {
        $rs[$q->id] = [
            'question' => $q->toArray(),
            'answer'   => $scores[$q->id],
        ];
    });

    return $rs;
}

现在您可以执行以下操作:

dd($survey->scores);

你会得到修饰的数据。

【讨论】:

    【解决方案2】:

    @Robert 您的回答是我的旧解决方案。现在,我有一个新的解决方案!这是我的答案。

    我们需要为 Foo 模型创建一个新的 trait。首先在 laravel 的“app”文件夹下创建“Relations”和“Relation”文件夹。 “Relation”文件夹位于“Relations”文件夹内。

    这个答案只是支持一维数组关系。你可以编辑这个完美...

    "app/Relations/Relation" 最后这是完整的文件夹路径结构。

    这是“app/Models/Foo.php”模型文件源。

        <?php
    
        namespace App\Models;
    
        use App\Relations\HasBarRelations;
        use Illuminate\Database\Eloquent\Model;
        use App\Models\Bar;
    
        class Foo extends Model
        {
            use HasBarRelations; // Trait
    
            protected $table = "foo";
            protected $fillable = ["bar"];
    
            protected $casts = [
                "bar" => "array" // Array cast
            ];
    
            public function bar()
            {
                return $this->belongsToBar(Bar::class, "id", "bar"); // Relationship
            }
        }
    
    

    还有“app/Relations/HasBarRelations.php”文件源;

        <?php
    
        namespace App\Relations;
    
        use App\Relations\Relation\BarRelation;
    
        trait HasBarRelations
        {
            /**
             * Define a custom relationship.
             *
             * @param string $related
             * @param null $foreignKey
             * @param null $localKey
             * @return HasBarRelations
             */
            public function belongsToBar($related, $foreignKey = null, $localKey = null)
            {
                $instance = new $related;
                $query = $instance->newQuery();
    
                return new BarRelation($query, $this, $foreignKey, $localKey);
            }
        }
    
    

    之后,在 Relation 文件夹下创建一个名为“app/Relation/BarRelation.php”的新文件。这个文件的源代码需要是这样的;

        <?php
    
        namespace App\Relations\Relation;
    
        use Closure;
        use Illuminate\Database\Eloquent\Relations\Relation;
        use Illuminate\Database\Eloquent\Builder;
        use Illuminate\Database\Eloquent\Collection;
        use Illuminate\Database\Eloquent\Model;
    
        class BarRelation extends Relation
        {
            /**
             * The baseConstraints callback
             *
             * @var string
             */
            protected $foreignKey;
            protected $localKey;
    
            /**
             * Create a new belongs to relationship instance.
             *
             * @param Builder $query
             * @param Model $parent
             * @param string $foreignKey
             * @param $localKey
             */
            public function __construct(Builder $query, Model $parent, $foreignKey, $localKey)
            {
                $this->foreignKey = $foreignKey;
                $this->localKey = $localKey;
    
                parent::__construct($query, $parent);
            }
    
            /**
             * Set the base constraints on the relation query.
             *
             * @return void
             */
            public function addConstraints()
            {
                if (static::$constraints) {
                    $this->query->where($this->foreignKey, '=', $this->getParentKey());
    
                    $this->query->whereNotNull($this->foreignKey);
                }
            }
    
            /**
             * Get the key value of the parent's local key.
             *
             * @return mixed
             */
            public function getParentKey()
            {
                return $this->parent->getAttribute($this->localKey);
            }
    
            /**
             * Set the constraints for an eager load of the relation.
             *
             * @param  array  $models
             * @return void
             */
            public function addEagerConstraints(array $models)
            {
                $key = $this->related->getTable().'.'.$this->foreignKey;
    
                $whereIn = $this->whereInMethod($this->related, $this->foreignKey);
    
                $this->query->{$whereIn}($key, $this->getEagerModelKeys($models));
            }
    
            /**
             * Gather the keys from an array of related models.
             *
             * @param  array  $models
             * @return array
             */
            protected function getEagerModelKeys(array $models)
            {
                $keys = [];
                // First we need to gather all of the keys from the parent models so we know what
                // to query for via the eager loading query. We will add them to an array then
                // execute a "where in" statement to gather up all of those related records.
                foreach ($models as $model) {
                    if (is_array($model->{$this->localKey}) && count($model->{$this->localKey}) > 0) {
                    foreach($model->{$this->localKey} as $ids){
                            $keys[] = $ids;
                        }
                    }
                }
                // If there are no keys that were not null we will just return an array with null
                // so this query wont fail plus returns zero results, which should be what the
                // developer expects to happen in this situation. Otherwise we'll sort them.
                if (count($keys) === 0) {
                    return [null];
                }
    
                sort($keys);
    
                return array_values(array_unique($keys));
            }
    
            /**
             * Initialize the relation on a set of models.
             *
             * @param  array   $models
             * @param  string  $relation
             * @return array
             */
            public function initRelation(array $models, $relation)
            {
                foreach ($models as $model) {
                    $model->setRelation($relation, $this->related->newCollection());
                }
    
                return $models;
            }
    
            /**
             * Match the eagerly loaded results to their parents.
             *
             * @param  array   $models
             * @param Collection $results
             * @param  string  $relation
             * @return array
             */
            public function match(array $models, Collection $results, $relation)
            {
                return $this->matchOneOrMany($models, $results, $relation, 'one');
            }
    
            /**
             * Build model dictionary keyed by the relation's foreign key.
             *
             * @param Collection $results
             * @return array
             */
            protected function buildDictionary(Collection $results)
            {
                $foreign = $this->foreignKey;
    
                return $results->mapToDictionary(function ($result) use ($foreign) {
                        return [$result->{$foreign} => $result];
                })->all();
            }
    
            protected function getRelationValue(array $dictionary, $key, $type)
            {
                $value = $dictionary[$key];
    
                return $type === 'one' ? reset($value) : $this->related->newCollection($value);
            }
    
            protected function matchOneOrMany(array $models, Collection $results, $relation, $type)
            {
                $dictionary = $this->buildDictionary($results);
    
                if(count($dictionary) > 0){
                    $data = [];
                    foreach ($models as $model) {
                        foreach($model->getAttribute($this->localKey) as $key){
                            if (array_key_exists($key, $dictionary)) {
                                $data[] = $this->getRelationValue($dictionary, $key, $type);
                                    $model->setRelation(
                                    $relation, collect(array_unique($data))
                                );
                            }
                        }
                    }
                }
    
                return $models;
            }
    
            /**
             * Get the results of the relationship.
             *
             * @return mixed
             */
            public function getResults()
            {
                return $this->get();
            }
    
            /**
             * Execute the query as a "select" statement.
             *
             * @param  array  $columns
             * @return Collection
             */
            public function get($columns = ['*'])
            {
                // First we'll add the proper select columns onto the query so it is run with
                // the proper columns. Then, we will get the results and hydrate out pivot
                // models with the result of those columns as a separate model relation.
                $columns = $this->query->getQuery()->columns ? [] : $columns;
    
                if ($columns == ['*']) {
                    $columns = [$this->related->getTable().'.*'];
                }
    
                $builder = $this->query->applyScopes();
    
                $models = $builder->addSelect($columns)->getModels();
    
                // If we actually found models we will also eager load any relationships that
                // have been specified as needing to be eager loaded. This will solve the
                // n + 1 query problem for the developer and also increase performance.
                if (count($models) > 0) {
                    $models = $builder->eagerLoadRelations($models);
                }
    
                return $this->related->newCollection($models);
            }
        }
    

    我们可以这样使用这些代码。

    $foo = Foo::with(["bar"])->where("id", 1)->first();
    $bars = $foo->bar(); // Result is the Eloquent Collection!
    $bar = $bars->first(); // Bar::class
    

    【讨论】:

      猜你喜欢
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 2021-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多