__placeholders) and $this->__placeholders); Trigger::current()->filter($filtered, $model_name."_".$name."_attr", $this); if ($filtered !== false) $this->$name = $filtered; $this->belongs_to = (array) $this->belongs_to; $this->has_many = (array) $this->has_many; $this->has_one = (array) $this->has_one; if (in_array($name, $this->belongs_to) or isset($this->belongs_to[$name])) { $class = (isset($this->belongs_to[$name])) ? $this->belongs_to[$name] : $name ; if (isset($this->belongs_to[$name])) { $opts =& $this->belongs_to[$name]; $model = oneof(@$opts["model"], $name); if (preg_match("/^\(([a-z0-9_]+)\)$/", $model, $match)) $model = $this->$match[1]; $match = oneof(@$opts["by"], strtolower($name)); fallback($opts["where"], array("id" => $this->{$match."_id"})); $opts["where"] = (array) $opts["where"]; foreach ($opts["where"] as &$val) if (preg_match("/^\(([a-z0-9_]+)\)$/", $val, $match)) $val = $this->$match[1]; fallback($opts["placeholders"], $placeholders); } else { $model = $name; $opts = array("where" => array("id" => $this->{$name."_id"})); } return $this->$name = new $model(null, $opts); } elseif (in_array($name, $this->has_many) or isset($this->has_many[$name])) { if (isset($this->has_many[$name])) { $opts =& $this->has_many[$name]; $model = oneof(@$opts["model"], depluralize($name)); if (preg_match("/^\(([a-z0-9_]+)\)$/", $model, $match)) $model = $this->$match[1]; $match = oneof(@$opts["by"], strtolower($name)); fallback($opts["where"], array($match."_id" => $this->id)); $opts["where"] = (array) $opts["where"]; foreach ($opts["where"] as &$val) if (preg_match("/^\(([a-z0-9_]+)\)$/", $val, $match)) $val = $this->$match[1]; fallback($opts["placeholders"], $placeholders); } else { $model = depluralize($name); $match = $model_name; $opts = array("where" => array(strtolower($match)."_id" => $this->id), "placeholders" => $placeholders); } return $this->$name = call_user_func(array($model, "find"), $opts); } elseif (in_array($name, $this->has_one) or isset($this->has_one[$name])) { if (isset($this->has_one[$name])) { $opts =& $this->has_one[$name]; $model = oneof(@$opts["model"], depluralize($name)); if (preg_match("/^\(([a-z0-9_]+)\)$/", $model, $match)) $model = $this->$match[1]; $match = oneof(@$opts["by"], strtolower($name)); fallback($opts["where"], array($match."_id" => $this->id)); $opts["where"] = (array) $opts["where"]; foreach ($opts["where"] as &$val) if (preg_match("/^\(([a-z0-9_]+)\)$/", $val, $match)) $val = $this->$match[1]; } else { $model = depluralize($name); $match = $model_name; $opts = array("where" => array(strtolower($match)."_id" => $this->id)); } return $this->$name = new $model(null, $opts); } if (isset($this->$name)) return $this->$name; } /** * Function: __getPlaceholders * Calls __get with the requested $name, but grabs everything as placeholders. * * Parameters: * $name - Name to call with. * * Returns: * @mixed@ * * See Also: * */ public function __getPlaceholders($name) { $this->__placeholders = true; $return = $this->__get($name); unset($this->__placeholders); return $return; } /** * Function: grab * Grabs a single model from the database. * * Parameters: * $model - The instantiated model class to pass the object to (e.g. Post). * $id - The ID of the model to grab. Can be null. * $options - An array of options, mostly SQL things. * * Options: * select - What to grab from the table. @(modelname)s@ by default. * from - Which table(s) to grab from? @(modelname)s.*@ by default. * left_join - A @LEFT JOIN@ associative array. Example: @array("table" => "foo", "where" => "foo = :bar")@ * where - A string or array of conditions. @array("__(modelname)s.id = :id")@ by default. * params - An array of parameters to pass to PDO. @array(":id" => $id)@ by default. * group - A string or array of "GROUP BY" conditions. * order - What to order the SQL result by. @__(modelname)s.id DESC@ by default. * offset - Offset for SQL query. * read_from - An array to read from instead of performing another query. */ protected static function grab($model, $id, $options = array()) { $model_name = strtolower(get_class($model)); if ($model_name == "visitor") $model_name = "user"; if (!isset($id) and isset($options["where"]["id"])) $id = $options["where"]["id"]; $cache = (is_numeric($id) and isset(self::$caches[$model_name][$id])) ? self::$caches[$model_name][$id] : ((isset($options["read_from"]["id"]) and isset(self::$caches[$model_name][$options["read_from"]["id"]])) ? self::$caches[$model_name][$options["read_from"]["id"]] : (isset(self::$caches[$model_name][serialize($id)]) ? self::$caches[$model_name][serialize($id)] : array())) ; # Is this model already in the cache? if (!empty($cache)) { foreach ($cache as $attr => $val) $model->$attr = $val; return; } fallback($options["select"], "*"); fallback($options["from"], ($model_name == "visitor" ? "users" : pluralize($model_name))); fallback($options["left_join"], array()); fallback($options["where"], array()); fallback($options["params"], array()); fallback($options["group"], array()); fallback($options["order"], "id DESC"); fallback($options["offset"], null); fallback($options["read_from"], array()); fallback($options["ignore_dupes"], array()); $options["where"] = (array) $options["where"]; $options["from"] = (array) $options["from"]; $options["select"] = (array) $options["select"]; if (is_numeric($id)) $options["where"]["id"] = $id; elseif (is_array($id)) $options["where"] = array_merge($options["where"], $id); $trigger = Trigger::current(); $trigger->filter($options, $model_name."_grab"); $sql = SQL::current(); if (!empty($options["read_from"])) $read = $options["read_from"]; else { $query = $sql->select($options["from"], $options["select"], $options["where"], $options["order"], $options["params"], null, $options["offset"], $options["group"], $options["left_join"]); $all = $query->fetchAll(); if (count($all) == 1) $read = $all[0]; else { $merged = array(); foreach ($all as $index => $row) foreach ($row as $column => $val) $merged[$row["id"]][$column][] = $val; foreach ($all as $index => &$row) $row = $merged[$row["id"]]; if (count($all)) { $keys = array_keys($all); $read = $all[$keys[0]]; foreach ($read as $name => &$column) { $column = (!in_array($name, $options["ignore_dupes"]) ? array_unique($column) : $column); $column = (count($column) == 1) ? $column[0] : $column ; } } else $read = false; } } if (!count($read) or !$read) return $model->no_results = true; else $model->no_results = false; foreach ($read as $key => $val) if (!is_int($key)) $model->$key = $val; if (isset($query) and isset($query->queryString)) $model->queryString = $query->queryString; if (isset($model->updated_at)) $model->updated = (!empty($model->updated_at) and $model->updated_at != "0000-00-00 00:00:00"); $clone = clone $model; self::$caches[$model_name][$read["id"]] = $clone; if (!is_numeric($id) and !isset($options["read_from"]["id"]) and $id !== null) self::$caches[$model_name][serialize($id)] = $clone; } /** * Function: search * Returns an array of model objects that are found by the $options array. * * Parameters: * $options - An array of options, mostly SQL things. * $options_for_object - An array of options for the instantiation of the model. * * Options: * select - What to grab from the table. @(modelname)s@ by default. * from - Which table(s) to grab from? @(modelname)s.*@ by default. * left_join - A @LEFT JOIN@ associative array. Example: @array("table" => "foo", "where" => "foo = :bar")@ * where - A string or array of conditions. @array("__(modelname)s.id = :id")@ by default. * params - An array of parameters to pass to PDO. @array(":id" => $id)@ by default. * group - A string or array of "GROUP BY" conditions. * order - What to order the SQL result by. @__(modelname)s.id DESC@ by default. * offset - Offset for SQL query. * limit - Limit for SQL query. * * See Also: * */ protected static function search($model, $options = array(), $options_for_object = array()) { $model_name = strtolower($model); fallback($options["select"], "*"); fallback($options["from"], pluralize(strtolower($model))); fallback($options["left_join"], array()); fallback($options["where"], null); fallback($options["params"], array()); fallback($options["group"], array()); fallback($options["order"], "id DESC"); fallback($options["offset"], null); fallback($options["limit"], null); fallback($options["placeholders"], false); fallback($options["ignore_dupes"], array()); $options["where"] = (array) $options["where"]; $options["from"] = (array) $options["from"]; $options["select"] = (array) $options["select"]; $trigger = Trigger::current(); $trigger->filter($options, pluralize(strtolower($model_name))."_get"); $grab = SQL::current()->select($options["from"], $options["select"], $options["where"], $options["order"], $options["params"], $options["limit"], $options["offset"], $options["group"], $options["left_join"])->fetchAll(); $shown_dates = array(); $results = array(); $rows = array(); foreach ($grab as $row) foreach ($row as $column => $val) $rows[$row["id"]][$column][] = $val; foreach ($rows as &$row) foreach ($row as $name => &$column) { $column = (!in_array($name, $options["ignore_dupes"]) ? array_unique($column) : $column); $column = (count($column) == 1) ? $column[0] : $column ; } foreach ($rows as $result) { if ($options["placeholders"]) { $results[] = $result; continue; } $options_for_object["read_from"] = $result; $result = new $model(null, $options_for_object); if (isset($result->created_at)) { $pinned = (isset($result->pinned) and $result->pinned); $shown = in_array(when("m-d-Y", $result->created_at), $shown_dates); $result->first_of_day = (!$pinned and !$shown and !AJAX); if (!$pinned and !$shown) $shown_dates[] = when("m-d-Y", $result->created_at); } $results[] = $result; } return ($options["placeholders"]) ? array($results, $model_name) : $results ; } /** * Function: delete * Deletes a given object. Calls the @delete_(model)@ trigger with the objects ID. * * Parameters: * $model - The model name. * $id - The ID of the object to delete. */ protected static function destroy($model, $id) { $model = strtolower($model); if (Trigger::current()->exists("delete_".$model)) Trigger::current()->call("delete_".$model, new $model($id)); SQL::current()->delete(pluralize($model), array("id" => $id)); } /** * Function: deletable * Checks if the can delete the post. */ public function deletable($user = null) { if ($this->no_results) return false; $name = strtolower(get_class($this)); fallback($user, Visitor::current()); return $user->group->can("delete_".$name); } /** * Function: editable * Checks if the can edit the post. */ public function editable($user = null) { if ($this->no_results) return false; $name = strtolower(get_class($this)); fallback($user, Visitor::current()); return $user->group->can("edit_".$name); } /** * Function: edit_link * Outputs an edit link for the model, if the visitor's edit_[model]. * * Parameters: * $text - The text to show for the link. * $before - If the link can be shown, show this before it. * $after - If the link can be shown, show this after it. * $classes - Extra CSS classes for the link, space-delimited. */ public function edit_link($text = null, $before = null, $after = null, $classes = "") { if (!$this->editable()) return false; fallback($text, __("Edit")); $name = strtolower(get_class($this)); if (@Feathers::$instances[$this->feather]->disable_ajax_edit) $classes = empty($classes) ? "no_ajax" : $classes." no_ajax" ; echo $before.''.$text.''.$after; } /** * Function: delete_link * Outputs a delete link for the post, if the delete_[model]. * * Parameters: * $text - The text to show for the link. * $before - If the link can be shown, show this before it. * $after - If the link can be shown, show this after it. * $classes - Extra CSS classes for the link, space-delimited. */ public function delete_link($text = null, $before = null, $after = null, $classes = "") { if (!$this->deletable()) return false; fallback($text, __("Delete")); $name = strtolower(get_class($this)); echo $before.''.$text.''.$after; } }