factory = $factory; return $this; } protected function validateDefinitions(array $definitions, array $minimum_requirements, array $default_values): array { foreach ($default_values as $key => $val) { if (!isset($definitions[$key])) { $definitions[$key] = $val; } } foreach ($minimum_requirements as $key) { if (!isset($definitions[$key])) { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); $class = get_called_class(); throw new InvalidArgumentException("Missing {$key} in {$trace[1]['function']} called in {$class}."); } } return $definitions; } public function parentOf(string $child_class, array $definitions): bool|array { $definitions = $this->validateDefinitions( $definitions, [Model::CHILD_KEY, Model::SELF_KEY], [Model::SELF_KEY => 'id'] ); $where = [[$definitions[Model::CHILD_KEY], $this->{$definitions[Model::SELF_KEY]}]]; return $this->factory->find($child_class)->where($where)->many(); } public function childOf(string $parent_class, array $definitions): bool|ModelInterface { $definitions = $this->validateDefinitions( $definitions, [Model::PARENT_KEY, Model::SELF_KEY], [Model::PARENT_KEY => 'id'] ); $where = [[$definitions[Model::PARENT_KEY], $this->{$definitions[Model::SELF_KEY]}]]; return $this->factory->find($parent_class)->where($where)->one(); } public function siblingOf(string $sibling_class, string $connecting_table, array $definitions): ?array { $definitions = $this->validateDefinitions( $definitions, [Model::SIBLING_KEY, Model::SIBLING_THROUGH_KEY, Model::SELF_THROUGH_KEY, Model::SELF_KEY], [Model::SIBLING_KEY => 'id', Model::SELF_KEY => 'id'] ); $sibling_table = (new $sibling_class())->getTable(); $joins = [ [ $connecting_table, "{$connecting_table}.{$definitions[Model::SIBLING_THROUGH_KEY]}", "{$sibling_table}.{$definitions[Model::SIBLING_KEY]}" ] ]; $where = [ [$definitions[Model::SIBLING_KEY], "{$connecting_table}.{$definitions[Model::SIBLING_THROUGH_KEY]}"], ["{$connecting_table}.{$definitions[Model::SELF_THROUGH_KEY]}", $this->{$definitions[Model::SELF_KEY]}] ]; return $this->factory ->find($sibling_class) ->join($joins) ->select("{$sibling_table}.*") ->where($where) ->many(); } public function toArray(): array { return $this->as_array(); } 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): bool|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 === false or $obj === null) { $obj = $factory->create($class, $data); } return $obj; } public function edit($input): ?array { $data = static::parseInput($input); foreach (static::$fields as $field) { if ($this->{$field} != $data[$field]) { $this->{$field} = $data[$field]; } } return $this->save(); } }