|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- <?php
- /**
- * Class: Model
- * The basis for the Models system.
- */
- class Model {
- # Array: $caches
- # Caches every loaded module into a clone of the object.
- static $caches = array();
-
- # Array: $belongs_to
- # An array of models that this Model belongs to.
- # This model should have a [modelname]_id column.
- public $belongs_to = array();
-
- # Array: $has_many
- # An array of models that belong to this Model.
- # They should have a [thismodel]_id column.
- public $has_many = array();
-
- # Array: $has_one
- # An array of models that this model has only one of.
- # The models should have a [thismodel]_id column.
- public $has_one = array();
-
- /**
- * Function: __get
- * Automatically handle model relationships when grabbing attributes of an object.
- *
- * Returns:
- * @mixed@
- */
- public function __get($name) {
- $model_name = get_class($this);
- $placeholders = (isset($this->__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 <Model.__get> with.
- *
- * Returns:
- * @mixed@
- *
- * See Also:
- * <Model.__get>
- */
- 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:
- * <Model.grab>
- */
- 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 <User> 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 <User> 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 <Group.can> 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.'<a href="'.Config::current()->chyrp_url.'/admin/?action=edit_'.$name.'&id='.$this->id.'" title="Edit" class="'.($classes ? $classes." " : '').$name.'_edit_link edit_link" id="'.$name.'_edit_'.$this->id.'">'.$text.'</a>'.$after;
- }
-
- /**
- * Function: delete_link
- * Outputs a delete link for the post, if the <User.can> 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.'<a href="'.Config::current()->chyrp_url.'/admin/?action=delete_'.$name.'&id='.$this->id.'" title="Delete" class="'.($classes ? $classes." " : '').$name.'_delete_link delete_link" id="'.$name.'_delete_'.$this->id.'">'.$text.'</a>'.$after;
- }
- }
|