factory = $factory; return $this; } protected function checkDefinitions(array $definitions, array $required, array $default) { foreach ($default as $key => $value) { if (!isset($definitions[$key])) { $definitions[$key] = $value; } } foreach ($required as $definition) { if (!isset($definitions[$definition])) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); throw new \InvalidArgumentException($definition . ' is required for ' . $trace[1]['function'] . ' in ' . get_called_class()); } } return $definitions; } public function parentOf(string $child_model_class, array $relation_definitions): ?array { $relation_definitions = $this->checkDefinitions($relation_definitions, [ Model::SELF_KEY, Model::CHILD_KEY ], [ Model::SELF_KEY => 'id' ]); return $this->factory ->find($child_model_class) ->where([ [ $relation_definitions[Model::CHILD_KEY], $this->{$relation_definitions[Model::SELF_KEY]} ] ]) ->many(); } public function childOf(string $parent_model_class, array $relation_definitions): ?ModelInterface { $relation_definitions = $this->checkDefinitions($relation_definitions, [ Model::SELF_KEY, Model::PARENT_KEY ], [ Model::PARENT_KEY => 'id' ]); $parent_table = (new $parent_model_class())->getTable(); return $this->factory ->find($parent_model_class) ->where([ [ $relation_definitions[Model::PARENT_KEY], $this->{$relation_definitions[Model::SELF_KEY]} ] ]) ->one(); } public function siblingOf(string $sibling_model_class, string $connecting_table, array $relation_definitions): ?array { $relation_definitions = $this->checkDefinitions($relation_definitions, [ Model::SELF_KEY, Model::SIBLING_KEY, Model::SELF_CONNECT_KEY, Model::SIBLING_CONNECT_KEY ], [ Model::SELF_KEY => 'id', Model::SIBLING_KEY => 'id' ]); $sibling_table = (new $sibling_model_class())->getTable(); return $this->find($sibling_model_class) ->select([ [ $sibling_table, '*' ], [ $connecting_table, '*' ] ]) ->join([ [ $connecting_table, implode('.', [ $connecting_table, $relation_definitions[Model::SIBLING_CONNECT_KEY] ]), implode('.', [ $sibling_table, $relation_definitions[Model::SIBLING_KEY] ]) ] ]) ->where([ [ implode('.', [ $connecting_table, $relation_definitions[Model::SELF_CONNECT_KEY] ]), $this->{$relation_definitions[Model::SELF_KEY]} ] ]) ->many(); } public function toArray(): array { return $this->asArray(); } protected static function parseInput($input): array { return array_intersect_key((array) $input, array_combine(static::$fields, static::$fields)); } public static function add(ModelFactory $factory, $input): ?ModelInterface { $data = static::parseInput($input); $class = get_called_class(); if (method_exists($class, 'find')) { $obj = static::find($factory, $input); } else { $where = $data; array_walk($where, function(&$item, $key) { $item = [$key, $item]; }); $where = array_values($where); $obj = $factory->find($class)->where($where)->one(); } if ($obj === null) { $obj = $factory->create($class, $data); } return $obj; } public function edit($input): bool { $data = static::parseInput($input); foreach (static::$fields as $field) { if ($this->{$field} != $data[$field]) { $this->{$field} = $data[$field]; } } return $this->save(); } }