public static function define($relationship, $options = null, $relationRef = null, $manyToManyPartial = null) { // Keep track of relationships that have already previously been processed. // Recursion can occur when defining M:M relationships, so this will prevent system hang-ups. static $processedRelationships = []; if (in_array($relationship, $processedRelationships)) { return null; } $processedRelationships[] = $relationship; // Vars if ($relationRef === null) { $relationRef = self::REF_DEFAULT; } // If more than one relationship is being defined here (ie. 1:M:1), break it down into // single relationships and parse each one separately. // Also, the resulting M:M relationship is defined. if (substr_count($relationship, ":") > 1) { // Extract components $components = explode(":", $relationship); $mSource = preg_replace("/\\(.+\$/", "", $components[0]); $mLink = preg_replace("/\\(.+\$/", "", $components[1]); $mTarget = preg_replace("/\\(.+\$/", "", $components[2]); $mSourceKey = $mTargetKey = null; if (strpos($mSource, ".")) { list($mSource, $mSourceKey) = explode(".", $mSource); } if (strpos($mTarget, ".")) { list($mTarget, $mTargetKey) = explode(".", $mTarget); } $mLinkSourceKey = Inflector::modelName_dbTableName($mSource) . '_id'; $mLinkTargetKey = Inflector::modelName_dbTableName($mTarget) . '_id'; if (strpos($mSource, ".") !== false) { list($mSource, $mSourceKey) = explode(".", $mSource); } if (strpos($mTarget, ".") !== false) { list($mTarget, $mTargetKey) = explode(".", $mTarget); } if (strpos($mLink, ".") !== false) { list($mLink, $mLinkSourceKey, $mLinkTargetKey) = explode(".", $mLink); } // Define the resulting M:M relationship self::$relationships[$mSource][$mTarget][$relationRef] = new ModelRelation(['modelSource' => $mSource, 'modelTarget' => $mTarget, 'cardinality' => ModelRelation::MANY_TO_MANY, 'modelLink' => $mLink, 'nativeKey' => $mSourceKey, 'foreignKey' => $mTargetKey]); if ($mSource != $mTarget) { self::$relationships[$mTarget][$mSource][$relationRef] = new ModelRelation(['modelSource' => $mTarget, 'modelTarget' => $mSource, 'cardinality' => ModelRelation::MANY_TO_MANY, 'modelLink' => $mLink, 'nativeKey' => $mSourceKey, 'foreignKey' => $mTargetKey]); self::define("{$components[0]}:{$mLink}.{$mLinkSourceKey}(M)", $options, $relationRef, $mTarget); self::define("{$mLink}.{$mLinkSourceKey}(M):{$components[0]}", $options, $relationRef, $mTarget); self::define("{$mLink}.{$mLinkTargetKey}(M):{$components[2]}", $options, $relationRef, $mSource); self::define("{$components[2]}{$mLink}.{$mLinkTargetKey}(M)", $options, $relationRef, $mSource); } else { self::$relationships[$mSource][$mTarget][self::REF_CHILD] = new ModelRelation(['modelSource' => $mSource, 'modelTarget' => $mTarget, 'cardinality' => ModelRelation::MANY_TO_MANY, 'modelLink' => $mLink, 'reference' => self::REF_CHILD, 'nativeKey' => $mSourceKey, 'foreignKey' => $mTargetKey]); self::define("{$components[0]}:{$mLink}.{$mLinkSourceKey}(M)", $options, self::REF_PARENT); self::define("{$mLink}.{$mLinkTargetKey}(M):{$components[2]}", $options, self::REF_CHILD); } //// Define the individual relationships, ie. 1:M and M:1 /*if($mSource!=$mTarget) { self::define("{$components[0]}:{$mLink}.{$mLinkSourceKey}(M)", $options, $relationRef, $mTarget); self::define("{$mLink}.{$mLinkSourceKey}(M):{$components[0]}", $options, $relationRef, $mTarget); self::define("{$mLink}.{$mLinkTargetKey}(M):{$components[2]}", $options, $relationRef, $mSource); self::define("{$components[2]}{$mLink}.{$mLinkTargetKey}(M)", $options, $relationRef, $mSource); } else { if($mLinkSourceKey==$mLinkTargetKey) { $mLinkSourceKey = "src_{$mLinkSourceKey}"; $mLinkTargetKey = "tgt_{$mLinkTargetKey}"; } //self::define("{$components[0]}:{$mLink}.{$mLinkSourceKey}(M)", $options, self::REF_PARENT); //self::define("{$mLink}.{$mLinkSourceKey}(M):{$components[0]}", $options, self::REF_PARENT); //self::define("{$mLink}.{$mLinkTargetKey}(M):{$components[2]}", $options, self::REF_CHILD); //self::define("{$components[2]}{$mLink}.{$mLinkTargetKey}(M)", $options, self::REF_CHILD); }*/ // Result return null; } // Extract components of the relationship into an ordered array preg_match_all("/([^:]*?)\\((.*?)\\)/", $relationship, $m); // $m_all = $m[0]; $m_model = $m[1]; $m_card = $m[2]; $m_key = [null, null]; $limit = [null, null]; if (strpos($m_model[0], ".") !== false) { list($m_model[0], $m_key[0]) = explode(".", $m_model[0]); } if (strpos($m_model[1], ".") !== false) { list($m_model[1], $m_key[1]) = explode(".", $m_model[1]); } // Create the ModelRelation instances if (!isset(self::$relationships[$m_model[0]][$m_model[1]][$relationRef])) { // Extract any limits imposed on the cardinality foreach ($m_card as $k => $v) { if (strpos($v, ",") !== false) { $limit[$k] = (int) preg_replace("/^.*?,([0-9]+)/", "\$1", $v); $m_card[$k] = preg_replace("/,.*/", "", $v); } } // Find the cardinality between the two Models $cardinalityMap = ['1:M' => ModelRelation::ONE_TO_MANY, 'M:1' => ModelRelation::MANY_TO_ONE, 'M:M' => ModelRelation::MANY_TO_MANY, '1:1' => ModelRelation::ONE_TO_ONE]; $cardinality = $cardinalityMap[strtoupper(implode(":", $m_card))]; $revCardinality = $cardinalityMap[strtoupper(implode(":", array_reverse($m_card)))]; // If a M:M cardinality is defined, we need to insert a linking table with an assumed name, // and then define the required 1:M:1 relationship. // We will assume that no keys have been defined in this M:M relationship, because it doesn't // make sense as the Models are not directly related. //$linkModel = NULL; if ($cardinality == ModelRelation::MANY_TO_MANY) { $linkModel = $m_model[0] . $m_model[1]; $relation = $m_model[0] . '(1):' . $linkModel . '(M):' . $m_model[1] . '(1)'; self::define($relation, $options, $relationRef); return null; } // If a 1:1 relation is defined, change it to a 1:M,1 if ($cardinality == self::ONE_TO_ONE) { $cardinality = self::ONE_TO_MANY; $revCardinality = self::MANY_TO_ONE; $limit = [1, 1]; } // For recursive 1:M relationships, make sure that the definition is 1:M, not M:1 if ($m_model[0] == $m_model[1] && $cardinality == self::MANY_TO_ONE) { $m_model = array_reverse($m_model); $cardinality = self::ONE_TO_MANY; $revCardinality = self::MANY_TO_ONE; $limit = array_reverse($limit); // Create the "M:1" relationship here (ref: self::REF_CHILD) self::$relationships[$m_model[1]][$m_model[0]][self::REF_CHILD] = new ModelRelation(['modelSource' => $m_model[1], 'modelTarget' => $m_model[0], 'cardinality' => $revCardinality, 'modelLink' => $manyToManyPartial, 'reference' => self::REF_CHILD, 'limit' => $limit[0], 'nativeKey' => $m_key[1], 'foreignKey' => $m_key[0], 'options' => $options]); } else { if ($m_model[0] == $m_model[1] && $cardinality == self::ONE_TO_MANY) { // Create the "M:1" relationship here (ref: self::REF_CHILD) self::$relationships[$m_model[1]][$m_model[0]][self::REF_CHILD] = new ModelRelation(['modelSource' => $m_model[1], 'modelTarget' => $m_model[0], 'cardinality' => $revCardinality, 'modelLink' => $manyToManyPartial, 'reference' => self::REF_CHILD, 'limit' => $limit[0], 'nativeKey' => $m_key[1], 'foreignKey' => $m_key[0], 'options' => $options]); } } // Extract options $options = $options === null ? null : explode(",", strtolower($options)); // Create relationships self::$relationships[$m_model[0]][$m_model[1]][$relationRef] = new ModelRelation(['modelSource' => $m_model[0], 'modelTarget' => $m_model[1], 'cardinality' => $cardinality, 'modelLink' => $manyToManyPartial, 'reference' => $relationRef, 'limit' => $limit[1], 'nativeKey' => $m_key[0], 'foreignKey' => $m_key[1], 'options' => $options]); if ($m_model[0] != $m_model[1] && $manyToManyPartial === null) { self::$relationships[$m_model[1]][$m_model[0]][$relationRef] = new ModelRelation(['modelSource' => $m_model[1], 'modelTarget' => $m_model[0], 'cardinality' => $revCardinality, 'modelLink' => $manyToManyPartial, 'reference' => $relationRef, 'limit' => $limit[0], 'nativeKey' => $m_key[1], 'foreignKey' => $m_key[0], 'options' => $options]); } } }
/** * Constructor. Never call this method directly. Instead use: * Buan\Model::create() * * Prepares the Model by setting a few basic properties: * - The model name * - The database table name * - The initial primary key value(s) (if applicable) * * @param string $modelName Name of the Model you want to create (UpperCamelCaps format) */ public function __construct($modelName = null) { // Store the Model's name // If the $modelName hasn't bee specified then we use the calling class' // name as the basis of the model name (basically remove the "Model" suffix) $this->modelName = $modelName === null ? preg_replace("/Model\$/", "", get_class($this)) : $modelName; // If the model isn't using it's own class, and it hasn't specified a // database table to use for storing it, then do some guess work based on // suggested conventions (ie. lower_underscored table names) if ($this->dbTableName === null) { $this->dbTableName = Inflector::modelName_dbTableName($this->modelName); } // Determine if this Model has a composite PK. This is considered the case // if $this->dbTablePrimaryKey has a comma-separated list of field names. $this->hasCompositePrimaryKey = strpos($this->getPrimaryKey(), ",") !== false; // Ensure the PK field(s) are preset to a NULL value. // SQLite requires that auto_increment fields have a NULL value in order // to increment correctly. if ($this->hasCompositePrimaryKey) { $keys = explode(",", $this->getPrimaryKey()); foreach ($keys as $key) { $this->setPrimaryKeyValue($key, null); } } else { $this->setPrimaryKeyValue(null); } }