Laravel施放关系

时间:2017-01-10 10:01:10

标签: php arrays laravel

我正在建立一个调查网站,用户可以提供0到10的答案。 我正在尝试演员将所有问题和答案存储在一行中。

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

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

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

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:1)

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

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

您将获得装饰数据。

答案 1 :(得分:0)

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

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

此答案仅支持一维数组关系。您可以编辑此文件以使其完美...

“ 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