2011-03-10 18 views
5

Tengo las siguientes tablas: carpetas, documentos, usuarios, docs_users. Doc pertenece a Binder, Doc tieneAndBelongsToMany usuario.Cómo filtrar asociaciones profundas en CakePHP

Quiero obtener carpetas y sus documentos asociados para el usuario que está actualmente conectado (el user_id asociado en la tabla docs_users).

He intentado con Containable y find ('all') con combinaciones, condiciones, etc. pero no puedo encontrar la manera de eliminar los documentos que provienen de usuarios que no están asociados en la tabla docs_users.

Este código no funciona:

$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); 

Y tampoco esto:

$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'); 

Cualquier ayuda de profesionales experimentados le sería genial! ¡Gracias!

¿Soluciones alternativas/simplificadas?

Esto funciona si solo quiero obtener carpetas a las que los usuarios tienen permisos. Corto y dulce. Sin embargo, aún enviará TODOS los documentos asociados, lo cual NO es el comportamiento que deseo. Solo debe transmitir los documentos a los que el usuario tiene permisos (como se describió anteriormente).

$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

Podría ayudar si se establece 'debug' a 2 y mira lo que está generando la torta de consulta. Luego realice los cambios necesarios para obtener el resultado deseado. – JohnP

+0

Además, podría pegar su esquema DB en pastebin o algo así y vincularlo. – JohnP

+0

Buena idea. Aquí está: http://pastebin.com/0bRn9USD No estoy usando las tablas de ACL en este momento porque creo que hacerlo en un nivel récord crearía una base de datos masiva. Un poco derrota el propósito. – dbme

Respuesta

4

Aquí está la solución final que surgió en base a todos los excelentes comentarios que recibí.Creo que esta es una solución elegante que puede reutilizarse en cualquier escenario donde se requieran asociaciones profundas.

En el control_bindler desvincula el modelo de documento y lo vuelvo a atar usando finderQuery para seleccionar solo los documentos que un usuario tiene permiso para ver. Luego, se unió a la tabla binders_users seleccionando solo los enlaces a los que los usuarios tienen permisos.

¡Gracias a todos por toda su ayuda!

$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') 
       ) 
      )    
     ) 
    ) 
); 

Más sobre binding/unbinding models

5
+0

Esos son excelentes enlaces. Definitivamente me llevo tan lejos. Pero no estoy seguro de que realmente respondan la pregunta. Creo que algún código relacionado realmente ayudaría aquí. – dbme

0

En esta línea, Necesito decirle a Cake de qué modelo está hablando:

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

e.g. DocsUser.id

... y no utiliza recursivo con contenido. Desaste de eso.

+0

En realidad eso es especificado por el árbol de la matriz. Si solo quería que fuera un nivel, tiene razón, podría especificar Doc.User.Docsuser.id = 17 pero, aun así, existe el mismo problema. – dbme

0

¿Has intentado entrar desde la perspectiva del usuario?

$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)); 

La asociación 'DocsUser' se debe detectar de todos modos.

Desde una perspectiva aglutinantes tal

En su enlazadores de modelos complemento (por favor marque los nombres de tablas, claves y modelo en caso de que hice un error tipográfico)

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); 
    } 

Luego, en sus carpetas CONTROLADOR tratar

$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))); 

¿Algo bueno?

+0

Esto definitivamente está en el camino correcto. Supongo que la pregunta es ¿cómo se hace esto en el contexto de un Binder? – dbme

+0

Así que sí. Funciona. Tuve que hacer un par de cambios (nombres de campo) pero funciona. Para no parecer un caballo de regalo en la boca, ¿puede darme algunas pistas sobre cómo podría abordar la visualización de TODAS las carpetas para las cuales el usuario tiene permisos y los documentos que pertenecen a la carpeta, para los cuales el usuario tiene permiso? Muchas gracias por su ayuda. ¡Súper esclarecedor! – dbme

+0

¿Cómo se representan los permisos? – Leo

Cuestiones relacionadas