/** * Add a hasAndBelongsToMany filter to a Zend_Db_Select object. * Example query: * SELECT * * FROM tags * LEFT JOIN tags_users ON tags_users.tag_id = tags.id AND user_id = 35 * INNER JOIN `users` ON users.id = tags_users.user_id * WHERE user_id 35 // in the case of negation, this'll be "WHERE user_id IS NULL" * * @param array $options Collection of options containing; * ['select'] Zend_Db_Select The select object * ['filterModel'] Garp_Model_Db The filtering model * ['filterColumn'] string The column used as the query filter * ['filterValue'] mixed The value used as the query filter * ['negation'] bool Wether the query should include or exclude * matches found by $filterValue * ['bidirectional'] bool Wether homophile relationships should be queried * bidirectionally * @return void */ protected function _addHasAndBelongsToManyClause(array $options) { if (!isset($options['bindingModel'])) { $modelNames = array($this->_model->getNameWithoutNamespace(), $options['filterModel']->getNameWithoutNamespace()); sort($modelNames); $bindingModelName = 'Model_' . implode('', $modelNames); } else { $bindingModelName = 'Model_' . $options['bindingModel']; } $bindingModel = new $bindingModelName(); $thisTableName = $this->_getTableName($this->_model); $bindingModelTable = $bindingModel->getName(); $reference = $bindingModel->getReference(get_class($this->_model)); foreach ($reference['refColumns'] as $i => $column) { if ($column === $options['filterColumn']) { $bindingModelForeignKeyField = $reference['columns'][$i]; $foreignKeyField = $column; break; } } $reference = $bindingModel->getReference(get_class($options['filterModel']), $this->_findSecondRuleKeyForHomophiles($options['filterModel'], $bindingModel)); foreach ($reference['refColumns'] as $i => $column) { if ($column === $options['filterColumn']) { $filterField = $reference['columns'][$i]; break; } } $bindingCondition = $bindingModelTable . '.' . $bindingModelForeignKeyField . ' = ' . $thisTableName . '.' . $foreignKeyField; $bindingCondition .= $bindingModel->getAdapter()->quoteInto(' AND ' . $bindingModelTable . '.' . $filterField . ' = ?', $options['filterValue']); if ($this->_isHomophile($options['filterModel']) && array_get($options, 'bidirectional')) { $bindingCondition .= ' OR ' . $bindingModelTable . '.' . $filterField . ' = ' . $thisTableName . '.' . $foreignKeyField; $bindingCondition .= $bindingModel->getAdapter()->quoteInto(' AND ' . $bindingModelTable . '.' . $bindingModelForeignKeyField . ' = ?', $options['filterValue']); } // Add columns of bindingTable to the query (namespaced using dot) $tmpBindingColumns = $bindingModel->info(Zend_Db_Table::COLS); $bindingColumns = array(); $weighableColumns = array(); if ($weighable = $bindingModel->getObserver('Weighable')) { $weighableColumns = $weighable->getWeightColumns(); } foreach ($tmpBindingColumns as $bc) { // Exclude foreign key fields if (in_array($bc, array($bindingModelForeignKeyField, $filterField))) { continue; } // Exclude columns generated by a Weighable behavior if (in_array($bc, $weighableColumns)) { continue; } $bindingColumns[$bindingModel->getNameWithoutNamespace() . '.' . $bc] = $bc; } $options['select']->joinLeft($bindingModelTable, $bindingCondition, $bindingColumns); if ($options['negation']) { $options['select']->where($bindingModelTable . '.' . $filterField . ' IS NULL'); } else { $options['select']->where($bindingModelTable . '.' . $filterField . ' = ?', $options['filterValue']); if ($this->_isHomophile($options['filterModel']) && array_get($options, 'bidirectional')) { $options['select']->orWhere($bindingModelTable . '.' . $bindingModelForeignKeyField . ' = ?', $options['filterValue']); $options['select']->group('id'); } } // Allow behaviors to modify the SELECT object $bindingModel->notifyObservers('beforeFetch', array($bindingModel, $options['select'])); }