/** * Ensures that given an association between * $bean1 and $bean2, * if one of them gets trashed the association will be * automatically removed. * @param RedBean_OODBBean $bean1 * @param RedBean_OODBBean $bean2 * @return boolean $addedFKS */ public static function addConstraint(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $dontCache = false) { //Fetch the toolbox $toolbox = RedBean_Setup::getToolBox(); RedBean_CompatManager::scanDirect($toolbox, array(RedBean_CompatManager::C_SYSTEM_MYSQL => "5")); //Create an association manager $association = new RedBean_AssociationManager($toolbox); $writer = $toolbox->getWriter(); $oodb = $toolbox->getRedBean(); $adapter = $toolbox->getDatabaseAdapter(); //Frozen? Then we may not alter the schema! if ($oodb->isFrozen()) { return false; } //$adapter->getDatabase()->setDebugMode(1); $table1 = $bean1->getMeta("type"); $table2 = $bean2->getMeta("type"); $table = $association->getTable(array($table1, $table2)); $idfield1 = $writer->getIDField($bean1->getMeta("type")); $idfield2 = $writer->getIDField($bean2->getMeta("type")); $bean = $oodb->dispense($table); $property1 = $bean1->getMeta("type") . "_id"; $property2 = $bean2->getMeta("type") . "_id"; if ($property1 == $property2) { $property2 = $bean2->getMeta("type") . "2_id"; } $table = $adapter->escape($table); $table1 = $adapter->escape($table1); $table2 = $adapter->escape($table2); $property1 = $adapter->escape($property1); $property2 = $adapter->escape($property2); //In Cache? Then we dont need to bother if (isset(self::$fkcache[$table])) { return false; } $db = $adapter->getCell("select database()"); $fks = $adapter->getCell("\n\t\t\tSELECT count(*)\n\t\t\tFROM information_schema.KEY_COLUMN_USAGE\n\t\t\tWHERE TABLE_SCHEMA ='{$db}' AND TABLE_NAME ='{$table}' AND\n\t\t\tCONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null\n\t\t"); //already foreign keys added in this association table if ($fks > 0) { return false; } //add the table to the cache, so we dont have to fire the fk query all the time. if (!$dontCache) { self::$fkcache[$table] = true; } $columns = $writer->getColumns($table); if ($writer->code($columns[$property1]) !== RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) { $writer->widenColumn($table, $property1, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32); } if ($writer->code($columns[$property2]) !== RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) { $writer->widenColumn($table, $property2, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32); } $sql = "\n\t\t\tALTER TABLE " . $writer->noKW($table) . "\n\t\t\tADD FOREIGN KEY({$property1}) references {$table1}(id) ON DELETE CASCADE;\n\n\t\t"; $adapter->exec($sql); $sql = "\n\t\t\tALTER TABLE " . $writer->noKW($table) . "\n\t\t\tADD FOREIGN KEY({$property2}) references {$table2}(id) ON DELETE CASCADE\n\t\t"; $adapter->exec($sql); return true; }
/** * Preloads certain properties for beans. * Understands aliases. * * Usage: $redbean->preload($books, array('coauthor'=>'author')); * * Usage for nested beans: * * $redbean->preload($texts, array('page', 'page.book', 'page.book.author')); * * preloads pages, books and authors. * You may also use a shortcut here: * * $redbean->preload($texts, array('page', '*.book', '*.author')); * * Can also load preload lists: * * $redbean->preload($books, array('ownPage'=>'page', '*.ownText'=>'text', 'sharedTag'=>'tag')); * * @param array $beans beans * @param array $types types to load */ public function preload($beans, $types, $closure = null) { if (is_string($types)) { $types = explode(',', $types); } $oldFields = array(); $i = 0; $retrievals = array(); $oldField = ''; foreach ($types as $key => $typeInfo) { list($type, $sqlObj) = is_array($typeInfo) ? $typeInfo : array($typeInfo, null); $map = $ids = $retrievals[$i] = array(); $field = is_numeric($key) ? $type : $key; //use an alias? if (strpos($field, '*') !== false) { $oldFields[] = $oldField; $field = str_replace('*', implode('.', $oldFields), $field); } if (strpos($field, '&') !== false) { $field = str_replace('&', implode('.', $oldFields), $field); } $filteredBeans = $beans; $counterID = 0; foreach ($filteredBeans as $bean) { $bean->setMeta('sys.input-bean-id', array($counterID => $counterID)); $counterID++; } while ($p = strpos($field, '.')) { //filtering: find the right beans in the path $nesting = substr($field, 0, $p); $filtered = array(); foreach ($filteredBeans as $bean) { $inputBeanID = $bean->getMeta('sys.input-bean-id'); if (is_array($bean->{$nesting})) { $nestedBeans = $bean->{$nesting}; foreach ($nestedBeans as $nestedBean) { $currentInputBeanIDs = $nestedBean->getMeta('sys.input-bean-id'); if (!is_array($currentInputBeanIDs)) { $currentInputBeanIDs = array(); } $addInputIDs = $bean->getMeta('sys.input-bean-id'); foreach ($addInputIDs as $addInputID) { $currentInputBeanIDs[$addInputID] = $addInputID; } $nestedBean->setMeta('sys.input-bean-id', $currentInputBeanIDs); } $filtered = array_merge($filtered, $nestedBeans); } elseif (!is_null($bean->{$nesting})) { $nestedBean = $bean->{$nesting}; $currentInputBeanIDs = $nestedBean->getMeta('sys.input-bean-id'); if (!is_array($currentInputBeanIDs)) { $currentInputBeanIDs = array(); } $addInputIDs = $bean->getMeta('sys.input-bean-id'); foreach ($addInputIDs as $addInputID) { $currentInputBeanIDs[$addInputID] = $addInputID; } $nestedBean->setMeta('sys.input-bean-id', $currentInputBeanIDs); $filtered[] = $bean->{$nesting}; } } $filteredBeans = $filtered; $field = substr($field, $p + 1); } $oldField = $field; if (strpos($type, '.')) { $type = $field; } if (strpos($field, 'shared') !== 0) { foreach ($filteredBeans as $bean) { //gather ids to load the desired bean collections if (strpos($field, 'own') === 0) { //based on bean->id for ownlist $id = $bean->id; $ids[$id] = $id; } elseif ($id = $bean->{$field . '_id'}) { //based on bean_id for parent $ids[$id] = $id; if (!isset($map[$id])) { $map[$id] = array(); } $map[$id][] = $bean; } } } if (strpos($field, 'shared') === 0) { $bean = reset($filteredBeans); $link = $bean->getMeta('type') . '_id'; $keys = $this->assocManager->related($filteredBeans, $type, true); $linkTable = $this->assocManager->getTable(array($type, $bean->getMeta('type'))); $linkBeans = $this->batch($linkTable, $keys); $linked = $targetIDs = array(); $targetIDField = $type . '_id'; foreach ($linkBeans as $linkBean) { $linkID = $linkBean->{$link}; if (!isset($linked[$linkID])) { $linked[$linkID] = array(); } $linked[$linkID][] = $linkBean; $targetIDs[$linkBean->{$targetIDField}] = $linkBean->{$targetIDField}; } $sharedBeans = $this->find($type, array('id' => $targetIDs), $sqlObj, !is_null($sqlObj)); foreach ($filteredBeans as $filteredBean) { $list = array(); if (isset($linked[$filteredBean->id])) { foreach ($linked[$filteredBean->id] as $linkBean) { foreach ($sharedBeans as $sharedBean) { if ($sharedBean->id == $linkBean->{$targetIDField}) { $list[$sharedBean->id] = $sharedBean; } } } } $filteredBean->setProperty($field, $list); $inputBeanIDs = $filteredBean->getMeta('sys.input-bean-id'); foreach ($inputBeanIDs as $inputBeanID) { if (!isset($retrievals[$i][$inputBeanID])) { $retrievals[$i][$inputBeanID] = array(); } foreach ($list as $listKey => $listBean) { $retrievals[$i][$inputBeanID][$listKey] = $listBean; } } } } elseif (strpos($field, 'own') === 0) { //preload for own-list using find $link = $bean->getMeta('type') . '_id'; $children = $this->find($type, array($link => $ids), $sqlObj, !is_null($sqlObj)); foreach ($filteredBeans as $filteredBean) { $list = array(); foreach ($children as $child) { if ($child->{$link} == $filteredBean->id) { $list[$child->id] = $child; } } $filteredBean->setProperty($field, $list); $inputBeanIDs = $filteredBean->getMeta('sys.input-bean-id'); foreach ($inputBeanIDs as $inputBeanID) { if (!isset($retrievals[$i][$inputBeanID])) { $retrievals[$i][$inputBeanID] = array(); } foreach ($list as $listKey => $listBean) { $retrievals[$i][$inputBeanID][$listKey] = $listBean; } } } } else { //preload for parent objects using batch() foreach ($this->batch($type, $ids) as $parent) { foreach ($map[$parent->id] as $childBean) { $childBean->setProperty($field, $parent); $inputBeanIDs = $childBean->getMeta('sys.input-bean-id'); foreach ($inputBeanIDs as $inputBeanID) { $retrievals[$i][$inputBeanID] = $parent; } } } } $i++; } if ($closure) { $key = 0; foreach ($beans as $bean) { $params = array(); foreach ($retrievals as $r) { $params[] = isset($r[$key]) ? $r[$key] : null; } array_unshift($params, $bean); call_user_func_array($closure, $params); $key++; } } }
/** * Ensures that given an association between * $bean1 and $bean2, * if one of them gets trashed the association will be * automatically removed. * @param RedBean_OODBBean $bean1 * @param RedBean_OODBBean $bean2 * @return boolean $addedFKS */ public static function addConstraint(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $dontCache = false) { $toolbox = RedBean_Setup::getToolBox(); RedBean_CompatManager::scanDirect($toolbox, array(RedBean_CompatManager::C_SYSTEM_MYSQL => "5", RedBean_CompatManager::C_SYSTEM_SQLITE => "3", RedBean_CompatManager::C_SYSTEM_POSTGRESQL => "7")); $association = new RedBean_AssociationManager($toolbox); $writer = $toolbox->getWriter(); $oodb = $toolbox->getRedBean(); $adapter = $toolbox->getDatabaseAdapter(); if ($oodb->isFrozen()) { return false; } $table1 = $bean1->getMeta("type"); $table2 = $bean2->getMeta("type"); $table = $association->getTable(array($table1, $table2)); $idfield1 = $writer->getIDField($bean1->getMeta("type")); $idfield2 = $writer->getIDField($bean2->getMeta("type")); $bean = $oodb->dispense($table); $property1 = $bean1->getMeta("type") . "_id"; $property2 = $bean2->getMeta("type") . "_id"; if ($property1 == $property2) { $property2 = $bean2->getMeta("type") . "2_id"; } $table = $adapter->escape($table); $table1 = $adapter->escape($table1); $table2 = $adapter->escape($table2); $property1 = $adapter->escape($property1); $property2 = $adapter->escape($property2); $fkCode = "fk" . md5($table . $property1 . $property2); if (isset(self::$fkcache[$fkCode])) { return false; } try { if ($writer instanceof RedBean_QueryWriter_PostgreSQL) { return self::constraintPostgreSQL($toolbox, $table, $table1, $table2, $property1, $property2, $dontCache); } if ($writer instanceof RedBean_QueryWriter_SQLite) { return self::constraintSQLite($toolbox, $table, $table1, $table2, $property1, $property2, $dontCache); } if ($writer instanceof RedBean_QueryWriter_MySQL) { return self::constraintMySQL($toolbox, $table, $table1, $table2, $property1, $property2, $dontCache); } } catch (RedBean_Exception_SQL $e) { if (!$writer->sqlStateIn($e->getSQLState(), array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE))) { throw $e; } } return false; }