在数据库设计中,常常会有用到以逗号隔开存储信息的场景
在我们的业务场景中,就会设计到很多 比如针对订单或者用户的标签记录,用户和标签一对多的关系 如果使用单表来维护标签关系 而实际业务场景不是特别复杂的时候,这样做显得有点多余,这时候经常会采用一个字段以特殊字符隔开记录标签或其他信息;
在laravel框架里,这个场景下,数据的回显如果采用join或者一对多关联的写法,并不适应数据的查询和回显及数据排重,如果有针对单字段,以特殊字符隔开的特殊关联就好了
下面我们给出针对单字段,根据特殊分隔字符,加载关联的代码实现
定义一个类 HasManyFromStr.php
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
class HasManyFromStr extends HasOneOrMany
{
protected $separator = ',';
public function __construct(Builder $query, Model $parent, string $foreignKey, string $localKey, $separator)
{
parent::__construct($query, $parent, $foreignKey, $localKey);
$this->separator = $separator;
}
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return ! is_null($this->getParentKey())
? $this->query->get()
: $this->related->newCollection();
}
/**
* 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;
}
/**
* 重写匹配方法
* @param array $models
* @param Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
$dictionary = $this->buildDictionary($results);
// Once we have the dictionary we can simply spin through the parent models to
// link them up with their children using the keyed dictionary to make the
// matching very convenient and easy work. Then we'll just return them.
foreach ($models as $model) {
$keys = $model->getAttribute($this->localKey);
$keys = explode($this->separator, $keys);
$keys = array_unique(array_filter($keys));
$type = 'one';
$relationResults = [];
foreach ($keys as $key) {
if (isset($dictionary[$key])) {
$temp = $this->getRelationValue($dictionary, $key, $type);
$relationResults[] = $temp;
}
}
$model->setRelation(
$relation, collect($relationResults)
);
}
return $models;
}
/**
* Get all of the primary keys for an array of models.
*
* @param array $models
* @param string $key
* @return array
*/
protected function getKeys(array $models, $key = null)
{
$keysArr = [];
collect($models)->map(function ($value) use ($key, &$keysArr) {
$result = $key ? $value->getAttribute($key) : $value->getKey();
$keysArr = array_merge($keysArr, explode(',', $result));
});
return collect($keysArr)->values()->filter()->unique()->sort()->all();
}
}
在基类的模型里,继承lavarel模型类, 实现以下方法 baseModel.php
/**
* Define a one-to-many relationship From a field with comma or other separator.
*
* @param $related
* @param null $foreignKey
* @param null $localKey
* @param string $separator
* @return HasManyFromStr
*/
public function hasManyFromStr($related, $foreignKey = null, $localKey = null, $separator = ',')
{
$instance = $this->newRelatedInstance($related);
$foreignKey = $foreignKey ?: $this->getForeignKey();
$localKey = $localKey ?: $this->getKeyName();
return $this->newHasManyFromStr(
$instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey, $separator
);
}
/**
* Instantiate a new HasManyFromStr relationship.
*
* @param Builder $query
* @param Model $parent
* @param $foreignKey
* @param $localKey
* @param string $separator
* @return HasManyFromStr
*/
protected function newHasManyFromStr(Builder $query, Model $parent, $foreignKey, $localKey, $separator = ',')
{
return new HasManyFromStr($query, $parent, $foreignKey, $localKey, $separator);
}
实际的模型类(继承上面的模型基类)中 的关联定义如下:
public function tagList()
{
return $this->hasManyFromStr(ErpTagModel::class, 'id', 'tag_id');
}
作者:linlancer
链接:https://www.jianshu.com/p/882aa5eec382