Как отфильтровать глубокие ассоциации в CakePHP - PullRequest
5 голосов
/ 11 марта 2011

У меня есть следующие таблицы: связыватели, документы, пользователи, docs_users. Док принадлежит Биндеру, у ДокА есть пользователь, принадлежащий пользователю.

Я хочу получить связыватели и связанные с ними документы для пользователя, который в данный момент вошел в систему (связанный идентификатор_пользователя в таблице docs_users).

Я пробовал Containable и находил ('all') с объединениями, условиями и т. Д., Но я не могу понять, как удалить Документы от Пользователей, которые не связаны в таблице docs_users.

Этот код НЕ работает:

$binders = $this->Binder->find( 
        'all',                  
        array( 
            'joins' => array(
                array( 
                    'table' => 'binders_users', 
                    'alias' => 'BindersUser', 
                    'type' => 'inner', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'BindersUser.binder_id = Binder.id',
                        'BindersUser.user_id = ' . $this->Auth->user('id')
                    )
                ),
                array( 
                    'table' => 'docs', 
                    'alias' => 'Doc', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'Doc.binder_id = Binder.id',
                    )
                ),                          
                array( 
                    'table' => 'docs_users', 
                    'alias' => 'DocsUser', 
                    'type' => 'left', 
                    'foreignKey' => false, 
                    'conditions'=> array(
                        'DocsUser.doc_id = Doc.id',
                        'DocsUser.user_id = ' . $this->Auth->user('id')
                    )
                )           
            ),
            'recursive'=>0
        )
    );
$this->set('binders', $binders);

И это тоже не так:

$this->Binder->recursive = 2;
$this->Binder->Behaviors->attach('Containable');
$this->Binder->contain(array(
    'Branch',
    'Doc' => array(                     
        'User' => array(
            'DocsUser' => array(
                'conditions' => array('id = "17"')
            )
        )
    )
));
$binders = $this->Binder->find('all');

Любая помощь от ваших опытных профессионалов была бы великолепна! Спасибо!

Альтернативные / упрощенные решения?

Это работает, если я просто хочу получить привязки, к которым у пользователей есть разрешения. Коротко и сладко. Тем не менее, он все равно будет отправлять ВСЕ связанные документы через, что НЕ является желаемым поведением. Он должен только передавать документы, на которые у пользователя есть разрешения (как описано ранее).

$binders = $this->Binder->find( 
    'all',                  
    array( 
        'joins' => array(
            array( 
                'table' => 'binders_users', 
                'alias' => 'BindersUser', 
                'type' => 'inner', 
                'foreignKey' => false, 
                'conditions'=> array(
                    'BindersUser.binder_id = Binder.id',
                    'BindersUser.user_id = ' . $this->Auth->user('id')
                )
            )
        )
    )
);

Ответы [ 4 ]

5 голосов
/ 11 марта 2011
4 голосов
/ 12 марта 2011

Вот окончательное решение, которое я придумал, основываясь на всех замечательных отзывах, которые я получил. Я думаю, что это элегантное решение, которое можно повторно использовать в любом сценарии, где требуются глубокие ассоциации.

В binder_controller я открепил модель Doc и привязал ее обратно, используя finderQuery, чтобы выбрать только Docs, которые пользователь имеет право просматривать. Затем присоединился к таблице binders_users, выбрав только те связыватели, к которым у пользователей есть разрешения.

Спасибо всем за помощь!

$this->Binder->unbindModel(array('hasMany' => array('Doc')));
$this->Binder->bindModel(
    array('hasMany' => array(
            'Doc' => array(
                'className' => 'Doc',
                'foreignKey' => 'binder_id',
                'dependent' => false,
                'finderQuery' => 'SELECT Doc.* FROM docs AS Doc INNER JOIN docs_users AS DocsUser ON DocsUser.doc_id = Doc.id AND DocsUser.user_id = ' . $this->Auth->user('id')
            )
        )
    )
);

$binders = $this->Binder->find( 
    'all',                  
    array( 
        'joins' => array(
            array( 
                'table' => 'binders_users', 
                'alias' => 'BindersUser', 
                'type' => 'inner', 
                'foreignKey' => false, 
                'conditions'=> array(
                    'BindersUser.binder_id = Binder.id',
                    'BindersUser.user_id = ' . $this->Auth->user('id')
                )
            )               
        )
    )
);

Подробнее о моделях переплета / развязки

0 голосов
/ 11 марта 2011

Вы пытались войти с точки зрения пользователя?

$this->Binder->Doc->User->Behaviors->attach('Containable');
$this->Binder->Doc->User->contain(array('Doc'=>'Binder'));
$user = $this->Binder->Doc->User->find('all',array('conditions'=>'User.id'=>$user_id));

Ассоциация 'DocsUser' должна быть обнаружена в любом случае.

С точки зрения Биндера, может быть

В вашей модели Binders добавить (пожалуйста, проверьте таблицу, ключ и названия модели на случай, если я сделал опечатку)

function getBindersByUserSql($user_id)
    {       
        $dbo = $this->getDataSource();
        $subQuery = $dbo->buildStatement(
            array(
                    'fields' => array('DISTINCT(Doc.binder_id)'),
                    'table' => "docs_users",                                       
                    'joins' => array(
                                array('table' => 'users',
                                    'alias' => 'User',
                                    'type' => 'INNER',
                                    'conditions' => array('DocsUser.user_id = User.id')
                                ),
                                array('table' => 'docs',
                                    'alias' => 'Doc',
                                    'type' => 'INNER',
                                    'conditions' => array('Doc.id = DocsUser.doc_id')
                                )
            ),
                    'alias'=>"DocsUser",                                            
                    'conditions' => array("User.id"=>$user_id),
                    'order' => null,
                    'group' => "Doc.binder_id"
                    ),
                    $this
                    );
        return $dbo->expression($subQuery);
    }

Тогда в ваших папках КОНТРОЛЛЕР попробуйте

$this->Binder->Behaviors->attach('Containable');
$this->Binder->contain(array('Doc'));
$conditions = array();
$conditions = $this->Binder->getBindersByUserSql($this->Auth->user('id'));
$binders = $this->Binder->find('all',array('conditions'=>$conditions)));

Хорошо?

0 голосов
/ 11 марта 2011

В этой строке вы должны указать Cake, какой идентификатор модели вы говорите:

'conditions' => array('id = "17"')

например. DocsUser.id

... и вы не используете рекурсив с переменным. Избавься от этого.

...