MySQL嵌套资源(数据透视表)查看/更新/管理权限

Luc*_*oni 1 php mysql database database-design laravel

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 |
+---------+-------------+
Run Code Online (Sandbox Code Playgroud)

动机: 如果有登录用户,说ID为1的用户,并且他/她想要查看任务(说ID为1的任务)那么我想确保ID为1 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jar*_*zyk 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);
Run Code Online (Sandbox Code Playgroud)

魔术发生在这里:

/**
 * 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);
}
Run Code Online (Sandbox Code Playgroud)

嘿,但那里发生了什么?

isRelatedTo() 像这样工作:

  1. 检查传递$id是模型还是只是一个id,并准备$related模型及其$id在回调中使用.如果你没有传递一个对象,那么Eloquent需要实例化$relations(relation1.relation2.relation3...)链上的所有相关模型以获得我们感兴趣的那个 - 这就是发生的事情getNestedRelated(),非常简单.

  2. 然后我们需要做这样的事情:

    // 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)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 因为我们不知道嵌套关系有多深,所以我们需要使用并发.然而我们将一个Closure传递给了whereHas,所以我们需要使用小技巧来调用它自己的体内(事实上我们不调用它,而是将它传递$callbackwhereHas方法,因为后者期望Closure为第二个param) - 这可能对那些不熟悉的匿名递归PHP函数很有用:

    // save it to the variable and pass it by reference
    $callback = function () use (&$callback) {
      if (...) // call the $callback again
      else // finish;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 我们也通过$relations引用传递给闭包(现在作为一个数组),以便取消它的元素,当我们得到它们(意思是我们嵌套whereHas)时,我们最终把where子句放在另一个上whereHas,来搜索我们的$related模型.

  5. 终于让我们回来了 bool