users transactions tasks
+----+--------+ +----+---------------+ +----+--------+
| id | name | | id | name | | id | name |
+----+--------+ +----+---------------+ +----+--------+
| 1 | User 1 | | 1 | Transaction 1 | | 1 | Task 1 |
| 2 | User 2 | | 2 | Transaction 2 | | 2 | Task 2 |
+----+--------+ +----+---------------+ +----+--------+
templates transaction_user task_transaction
+----+---------------+ +---------+----------------+ +---------+----------------+
| id | name | | user_id | transaction_id | | task_id | transaction_id |
+----+---------------+ +---------+----------------+ +---------+----------------+
| 1 | Template 1 | | 1 | 1 | | 1 | 1 |
| 2 | Template 2 | | 2 | 2 | +---------+----------------+
+----+---------------+ +---------+----------------+
task_template
+---------+-------------+
| task_id | template_id |
+---------+-------------+
| 2 | 2 |
+---------+-------------+
动机:
如果有登录用户,说ID为1的用户,并且他/她想要查看任务(比如ID为1的任务),那么我想确保ID为Belongs to
的任务用户在我让他看之前。此外,我还需要向用户显示属于他的所有任务。任务只是一个模型..我需要为所有模型处理这个。我在下面分享了我的代码,我是否努力了?
我可能在这里省略了一些细节,所以请随时提问。 感谢。
代码
<?php namespace SomeProject\Repositories;
use User;
use Account;
use Task;
use Document;
use Transaction;
use Property;
use DB;
use Respond;
abstract class DbRepository
{
/**
* The many to many relationships are handeled using pivot tables
* We will use this array to figure out relationships and then get
* a particular resource's owner / account
*/
public $pivot_models = array(
'Task' => array(
'Transaction' => 'task_transaction'
),
'Transaction' => array(
'User' => 'transaction_user'
),
'Document' => array(
'Property' => 'document_property',
'Task' => 'document_task',
'Message' => 'document_message'
)
);
public $entity_ids;
public function getOwnersByEntity(array $ids, $entity)
{
$this->entity_ids = [];
$user_ids = [];
$entity = ucfirst(strtolower($entity)); // arrays keys are case sensitive
if( $this->getPivotIds($ids, $entity) )
{
foreach ($this->entity_ids as $entity_name => $entity_ids_arr)
{
$entity_name_lowercase = strtolower($entity_name);
if($entity_name_lowercase != 'user')
{
$user_ids_from_entity = $entity_name::whereIn('id', $entity_ids_arr)
->lists('user_id');
}
else
{
// We already have the IDs if the entity is User
$user_ids_from_entity = $entity_ids_arr;
}
array_push($user_ids, $user_ids_from_entity);
}
$merged_user_ids = call_user_func_array('array_merge', $user_ids);
return array_unique($merged_user_ids);
}
else
{
return $entity::whereIn('id', $ids)->lists('user_id');
}
}
public function getPivotIds(array $ids, $entity)
{
$entity_lowercase = strtolower($entity);
if( array_key_exists($entity, $this->pivot_models) )
{
// Its a pivot model
foreach ($this->pivot_models[$entity] as $related_model => $table) // Transaction, Template
{
$related_model_lowercase = strtolower($related_model);
$this->entity_ids[$related_model] = DB::table($table)
->whereIn($entity_lowercase . '_id', $ids)
->lists($related_model_lowercase . '_id');
if( $this->getPivotIds($this->entity_ids[$related_model], $related_model) )
{
unset($this->entity_ids[$related_model]);
}
}
return true;
}
return false;
}
}
答案 0 :(得分:6)
要检查给定的模型是否与另一个模型相关,如果我找到你就是你想要的,你需要的只是这个微小的方法充分利用Eloquent
:
(在BaseModel
,Entity
或范围内实施,适用于您的任何内容)
// usage
$task->isRelatedTo('transactions.users', $id);
// or
$template->isRelatedTo('tasks.transactions.users', Auth::user());
// or any kind of relation:
// imagine this: User m-m Transaction 1-m Item m-1 Group
$group->isRelatedTo('items.transaction.users', $id);
魔术发生在这里:
/**
* Check if it is related to any given model through dot nested relations
*
* @param string $relations
* @param int|\Illuminate\Database\Eloquent\Model $id
* @return boolean
*/
public function isRelatedTo($relations, $id)
{
$relations = explode('.', $relations);
if ($id instanceof Model)
{
$related = $id;
$id = $related->getKey();
}
else
{
$related = $this->getNestedRelated($relations);
}
// recursive closure
$callback = function ($q) use (&$callback, &$relations, $related, $id)
{
if (count($relations))
{
$q->whereHas(array_shift($relations), $callback);
}
else
{
$q->where($related->getQualifiedKeyName(), $id);
}
};
return (bool) $this->whereHas(array_shift($relations), $callback)->find($this->getKey());
}
protected function getNestedRelated(array $relations)
{
$models = [];
foreach ($relations as $key => $relation)
{
$parent = ($key) ? $models[$key-1] : $this;
$models[] = $parent->{$relation}()->getRelated();
}
return end($models);
}
isRelatedTo()
的工作原理如下:
检查传递$id
是模型还是ID,并准备$related
模型及其$id
以用于回调。如果你没有传递一个对象,那么Eloquent需要在$relations
(relation1.relation2.relation3...
)链上实例化所有相关模型以获得我们感兴趣的模型 - 这就是getNestedRelated()
中发生的情况,非常简单。
然后我们需要做这样的事情:
// assuming relations 'relation1.relation2.relation3'
$this->whereHas('relation1', function ($q) use ($id) {
$q->whereHas('relation2', function ($q) use ($id) {
$q->whereHas('relation3', function ($q) use ($id) {
$q->where('id', $id);
});
});
})->find($this->getKey());
// returns new instance of current model or null, thus cast to (bool)
因为我们不知道嵌套关系有多深,所以我们需要使用并发。然而我们将一个Closure传递给whereHas
,所以我们需要使用小技巧来调用它自己的体内(事实上我们不会调用它,而是将它作为$callback
传递给它whereHas
方法,因为后者希望Closure成为第二个参数) - 这可能对那些不熟悉的Anonymous recursive PHP functions 有用:
// save it to the variable and pass it by reference
$callback = function () use (&$callback) {
if (...) // call the $callback again
else // finish;
}
我们也通过引用传递给闭包$relations
(现在作为一个数组)以便取消它的元素,当我们得到它们时(意思是我们嵌套whereHas
),我们最后使用where
子句而不是其他whereHas
来搜索我们的$related
模型。
最后让我们返回bool
答案 1 :(得分:1)
真的没有简单或规范的方式,但这是我尝试做的一个原始例子。
class Entity extends Eloquent {
public function isRelatedTo($instance, $through)
{
$column = $instance->joiningTable($through) . '.' . $instance->getForeignKey();
$query = DB::table('');
this->buildJoin($query, $instance, $through);
return $query->where($column, '=', $instance->getKey())->exists();
}
public function relatesToMany($related, $through)
{
$that = $this;
$related = new $related;
return $related->whereIn($related->getKeyName(), function($query) use ($that, $related, $through) {
$that->buildJoin($query, $related, $through);
})->get();
}
protected function buildJoin($query, $related, $through)
{
$through = new $through;
$this_id = $this->getForeignKey();
$related_id = $related->getForeignKey();
$through_id = $through->getForeignKey();
$this_pivot = $this->joiningTable($through);
$related_pivot = $related->joiningTable($through);
$query->select($related_pivot . '.' . $related_id)->from($related_pivot)
->join($this_pivot, $related_pivot . '.' . $through_id, '=', $this_pivot . '.' . $through_id)
->where($this_pivot . '.' . $this_id, '=', $this->getKey());
}
}
然后,对于您的用例:
class User extends Entity {
public function isOwnerOf($task)
{
return $this->isRelatedTo($task, 'Transaction');
}
public function tasks()
{
return $this->relatesToMany('Task', 'Transaction');
}
}
免责声明:代码尚未经过测试。
请注意,在这个非常简单的示例中,relatesToMany
直接返回一个Collection。为了获得更多优势,它可以返回一个你自己的Eloquent的Relation类扩展的实例 - 这显然需要更长的时间来实现。
添加对多个中间实体的支持应该不难;您可能希望$through
参数可能是一个数组,然后相应地构建多连接查询。
答案 2 :(得分:-2)
如果这是一个Laravel项目,那么是的,你的尝试太难了。
如果您打算使用Laravel,建议您使用Laravel提供的功能,即ORM,Eloquent,以及它捆绑的Schema工具。我建议您查看Laravel's Getting Started page in their documentation,以便正确设置项目以使用Eloquent。
如果你在他们的模型中阅读the basics of how Eloquent handles relations,那也是有益的,因为他们完成了你正在尝试的所有工作。