php - MySQL nested resources (pivot tables) permission to view/update/manage -
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 | +---------+-------------+
motive: if there logged in user, user id 1, , he/she wants see task (say task id 1) want make sure task id 1 belongs to
user before let him view it. need someway show user tasks belong him. task 1 model.. need handle models. have shared code below, trying hard?
i may have omitted details here please feel free ask questions. thanks.
code
<?php namespace someproject\repositories; use user; use account; use task; use document; use transaction; use property; use db; use respond; abstract class dbrepository { /** * many many relationships handeled using pivot tables * use array figure out relationships , * 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 case sensitive if( $this->getpivotids($ids, $entity) ) { foreach ($this->entity_ids $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 { // have ids if entity 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) ) { // pivot model foreach ($this->pivot_models[$entity] $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; } }
to check if given model related one, want if right, need tiny method making of eloquent
:
(implement in basemodel
, entity
or scope, whatever suits you)
// usage $task->isrelatedto('transactions.users', $id); // or $template->isrelatedto('tasks.transactions.users', auth::user()); // or kind of relation: // imagine this: user m-m transaction 1-m item m-1 group $group->isrelatedto('items.transaction.users', $id);
the magic happens here:
/** * check if related 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 $key => $relation) { $parent = ($key) ? $models[$key-1] : $this; $models[] = $parent->{$relation}()->getrelated(); } return end($models); }
hey, what's going on there?
isrelatedto()
works this:
check if passed
$id
model or id, , prepares$related
model ,$id
use in callback. if don't pass object eloquent needs instantiate related models on$relations
(relation1.relation2.relation3...
) chain 1 interested in - that's happens ingetnestedrelated()
, pretty straightforward.then need this:
// 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, cast (bool)
since don't know how relation nested, need use recurrency. pass closure
wherehas
, need use little trick in order call inside body (in fact don't call it, rather pass$callback
wherehas
method, since latter expects closure 2nd param) - this might useful unfamiliar anonymous recursive php functions:// save variable , pass reference $callback = function () use (&$callback) { if (...) // call $callback again else // finish; }
we pass closure
$relations
(as array now) reference in order unshift elements, , when got them (meaning nestedwherehas
), putwhere
clause instead ofwherehas
, search our$related
model.finally let's return
bool
Comments
Post a Comment