/** * Returns a F0FDatabaseIterator based on a given relation * * @param array $relation Indexed array holding relation definition. * tableClass => name of the related table class * localKey => name of the local key * remoteKey => name of the remote key * pivotTable => name of the pivot table (optional) * theirPivotKey => name of the remote key in the pivot table (mandatory if pivotTable is set) * ourPivotKey => name of our key in the pivot table (mandatory if pivotTable is set) * * @return F0FDatabaseIterator * * @throws RuntimeException * @throws InvalidArgumentException */ protected function getIteratorFromRelation($relation) { // Sanity checks if (!isset($relation['tableClass']) || !isset($relation['remoteKey']) || !isset($relation['localKey']) || !$relation['tableClass'] || !$relation['remoteKey'] || !$relation['localKey']) { throw new InvalidArgumentException('Missing array index for the ' . __METHOD__ . ' method. Please check method signature', 500); } if (array_key_exists('pivotTable', $relation)) { if (!isset($relation['theirPivotKey']) || !isset($relation['ourPivotKey']) || !$relation['pivotTable'] || !$relation['theirPivotKey'] || !$relation['ourPivotKey']) { throw new InvalidArgumentException('Missing array index for the ' . __METHOD__ . ' method. Please check method signature', 500); } } // Get a table object from the table class name $tableClass = $relation['tableClass']; $tableClassParts = F0FInflector::explode($tableClass); if (count($tableClassParts) < 3) { throw new InvalidArgumentException('Invalid table class named. It should be something like FooTableBar'); } $table = F0FTable::getInstance($tableClassParts[2], ucfirst($tableClassParts[0]) . ucfirst($tableClassParts[1])); // Get the table name $tableName = $table->getTableName(); // Get the remote and local key names $remoteKey = $relation['remoteKey']; $localKey = $relation['localKey']; // Get the local key's value $value = $this->table->{$localKey}; // If there's no value for the primary key, let's stop here if (!$value) { throw new RuntimeException('Missing value for the primary key of the table ' . $this->table->getTableName(), 500); } // This is required to prevent one relation from killing the db cursor used in a different relation... $oldDb = $this->table->getDbo(); $oldDb->disconnect(); // YES, WE DO NEED TO DISCONNECT BEFORE WE CLONE THE DB OBJECT. ARGH! $db = clone $oldDb; // Begin the query $query = $db->getQuery(true)->select('*')->from($db->qn($tableName)); // Do we have a pivot table? $hasPivot = array_key_exists('pivotTable', $relation); // If we don't have pivot it's a straightforward query if (!$hasPivot) { $query->where($db->qn($remoteKey) . ' = ' . $db->q($value)); } else { $subQuery = $db->getQuery(true)->select($db->qn($relation['theirPivotKey']))->from($db->qn($relation['pivotTable']))->where($db->qn($relation['ourPivotKey']) . ' = ' . $db->q($value)); $query->where($db->qn($remoteKey) . ' IN (' . $subQuery . ')'); } $db->setQuery($query); $cursor = $db->execute(); $iterator = F0FDatabaseIterator::getIterator($db->name, $cursor, null, $tableClass); return $iterator; }
/** * Save fields for many-to-many relations in their pivot tables. * * @param F0FTable $table Current item table. * * @return bool True if the object can be saved successfully, false elsewhere. * @throws Exception The error message get trying to save fields into the pivot tables. */ public function onAfterStore(&$table) { // Retrieve the relations configured for this table $input = new F0FInput(); $key = $table->getConfigProviderKey() . '.relations'; $relations = $table->getConfigProvider()->get($key, array()); // Abandon the process if not a save task if (!in_array($input->getWord('task'), array('apply', 'save', 'savenew'))) { return true; } // For each relation check relative field foreach ($relations as $relation) { // Only if it is a multiple relation, sure! if ($relation['type'] == 'multiple') { // Retrive the fully qualified relation data from F0FTableRelations object $relation = array_merge(array('itemName' => $relation['itemName']), $table->getRelations()->getRelation($relation['itemName'], $relation['type'])); // Deduce the name of the field used in the form $field_name = F0FInflector::pluralize($relation['itemName']); // If field exists we catch its values! $field_values = $input->get($field_name, array(), 'array'); // If the field exists, build the correct pivot couple objects $new_couples = array(); foreach ($field_values as $value) { $new_couples[] = array($relation['ourPivotKey'] => $table->getId(), $relation['theirPivotKey'] => $value); } // Find existent relations in the pivot table $query = $table->getDbo()->getQuery(true)->select($relation['ourPivotKey'] . ', ' . $relation['theirPivotKey'])->from($relation['pivotTable'])->where($relation['ourPivotKey'] . ' = ' . $table->getId()); $existent_couples = $table->getDbo()->setQuery($query)->loadAssocList(); // Find new couples and create its foreach ($new_couples as $couple) { if (!in_array($couple, $existent_couples)) { $query = $table->getDbo()->getQuery(true)->insert($relation['pivotTable'])->columns($relation['ourPivotKey'] . ', ' . $relation['theirPivotKey'])->values($couple[$relation['ourPivotKey']] . ', ' . $couple[$relation['theirPivotKey']]); // Use database to create the new record if (!$table->getDbo()->setQuery($query)->execute()) { throw new Exception('Can\'t create the relation for the ' . $relation['pivotTable'] . ' table'); } } } // Now find the couples no more present, that will be deleted foreach ($existent_couples as $couple) { if (!in_array($couple, $new_couples)) { $query = $table->getDbo()->getQuery(true)->delete($relation['pivotTable'])->where($relation['ourPivotKey'] . ' = ' . $couple[$relation['ourPivotKey']])->where($relation['theirPivotKey'] . ' = ' . $couple[$relation['theirPivotKey']]); // Use database to create the new record if (!$table->getDbo()->setQuery($query)->execute()) { throw new Exception('Can\'t delete the relation for the ' . $relation['pivotTable'] . ' table'); } } } } } return true; }