我正在建立一个调查网站,用户可以提供0到10的答案。 我正在尝试演员将所有问题和答案存储在一行中。
这就是数据库中分数字段的样子
{"5":8,"6":8}
这是预期的。 “5”和“6”是问题的ID,8是用户提供的分数。
我想知道是否可以查询答案但是包含与问题的关系。我希望看到如下结果
{"5" => {"question: ...", "answer: ..."}
所以我的问题是:是否可以转换为数组但在Laravel中包含关系?或者我是否需要循环数组并获取每行的问题数据。
谢谢!
答案 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