Ejemplo n.º 1
0
 /**
  * Match a number of children count
  *
  */
 protected function getQueryNumChildren(DatabaseQuerySelect $query, $selector)
 {
     if (!in_array($selector->operator, array('=', '<', '>', '<=', '>=', '!='))) {
         throw new WireException("Operator '{$selector->operator}' not allowed for 'num_children' selector.");
     }
     if ($this->getQueryNumChildren) {
         throw new WireException("You may only have one 'children.count' selector per query");
     }
     $value = (int) $selector->value;
     $this->getQueryNumChildren++;
     $n = $this->getQueryNumChildren;
     if (in_array($selector->operator, array('<', '<=', '!=')) && $value || ($selector->operator == '=' || $selector->operator == '>=') && !$value) {
         // allow for zero values
         $query->select("count(pages_num_children{$n}.id) AS num_children{$n}");
         $query->leftjoin("pages AS pages_num_children{$n} ON (pages_num_children{$n}.parent_id=pages.id)");
         $query->groupby("HAVING count(pages_num_children{$n}.id){$selector->operator}{$value}");
     } else {
         // non zero values
         $query->select("pages_num_children{$n}.num_children{$n} AS num_children{$n}");
         $query->leftjoin("(" . "SELECT p{$n}.parent_id, count(p{$n}.id) AS num_children{$n} " . "FROM pages AS p{$n} " . "GROUP BY p{$n}.parent_id " . "HAVING num_children{$n}{$selector->operator}{$value}" . ") pages_num_children{$n} ON pages_num_children{$n}.parent_id=pages.id");
         $query->where("pages_num_children{$n}.num_children{$n}{$selector->operator}{$value}");
     }
 }
Ejemplo n.º 2
0
 /**
  * Get the DatabaseQuerySelect to perform the load operation of items
  *
  * @param WireArray $items
  * @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all. 
  * @return DatabaseQuerySelect
  *
  */
 protected function getLoadQuery($selectors = null)
 {
     $item = $this->makeBlankItem();
     $fields = array_keys($item->getTableData());
     $table = $this->getTable();
     foreach ($fields as $k => $v) {
         $fields[$k] = "{$table}.{$v}";
     }
     $query = new DatabaseQuerySelect();
     $query->select($fields)->from($table);
     if ($sort = $this->getSort()) {
         $query->orderby($sort);
     }
     $this->getLoadQuerySelectors($selectors, $query);
     return $query;
 }
Ejemplo n.º 3
0
 /**
  * Return the query used for loading all parts of the data from this field
  *
  * @param Field $field
  * @param DatabaseQuerySelect $query
  * @return DatabaseQuerySelect
  *
  */
 public function getLoadQuery(Field $field, DatabaseQuerySelect $query)
 {
     $table = $field->table;
     $schema = $this->trimDatabaseSchema($this->getDatabaseSchema($field));
     $fieldName = $field->name;
     // now load any extra components (if applicable) in a fieldName__SubfieldName format.
     foreach ($schema as $k => $v) {
         $query->select("{$table}.{$k} AS `{$fieldName}__{$k}`");
     }
     return $query;
 }
 /**
  * Get the query that matches a Fieldtype table's data with a given value
  *
  * Possible template method: If overridden, children should NOT call this parent method. 
  *
  * @param DatabaseQuerySelect $query
  * @param string $table The table name to use
  * @param string $subfield Name of the field (typically 'data', unless selector explicitly specified another)
  * @param string $operator The comparison operator
  * @param mixed $value The value to find
  * @return DatabaseQuery $query
  *
  */
 public function getMatchQuery($query, $table, $subfield, $operator, $value)
 {
     self::$getMatchQueryCount++;
     $n = self::$getMatchQueryCount;
     $field = $query->field;
     $database = $this->wire('database');
     $table = $database->escapeTable($table);
     if ($subfield === 'count' && (empty($value) || ctype_digit(ltrim("{$value}", '-'))) && in_array($operator, array("=", "!=", ">", "<", ">=", "<="))) {
         $value = (int) $value;
         $t = $table . "_" . $n;
         $c = $database->escapeTable($this->className()) . "_" . $n;
         $query->select("{$t}.num_{$t} AS num_{$t}");
         $query->leftjoin("(" . "SELECT {$c}.pages_id, COUNT({$c}.pages_id) AS num_{$t} " . "FROM " . $database->escapeTable($field->table) . " AS {$c} " . "GROUP BY {$c}.pages_id " . ") {$t} ON {$t}.pages_id=pages.id");
         if (in_array($operator, array('<', '<=', '!=')) && $value || in_array($operator, array('>', '>=')) && $value < 0 || in_array($operator, array('=', '>=')) && !$value) {
             // allow for possible zero values
             $query->where("(num_{$t}{$operator}{$value} OR num_{$t} IS NULL)");
             // QA
         } else {
             // non zero values
             $query->where("num_{$t}{$operator}{$value}");
             // QA
         }
         // only allow matches using templates with the requested field
         $sql = 'pages.templates_id IN(';
         foreach ($field->getTemplates() as $template) {
             $sql .= (int) $template->id . ',';
         }
         $sql = rtrim($sql, ',') . ')';
         $query->where($sql);
         // QA
     } else {
         $query = parent::getMatchQuery($query, $table, $subfield, $operator, $value);
     }
     return $query;
 }
 /**
  * Perform a partial match on title of options
  * 
  * @param Field $field
  * @param string $property Either 'title' or 'value'
  * @param string $operator
  * @param string $value Value to find
  * @return SelectableOptionArray
  * 
  */
 public function findOptionsByProperty(Field $field, $property, $operator, $value)
 {
     if ($operator == '=' || $operator == '!=') {
         // no need to use fulltext matching if operator is not a partial match operator
         return $this->getOptions($field, array($property => $value));
     }
     $query = new DatabaseQuerySelect();
     $query->select('*');
     $query->from(self::optionsTable);
     $query->where("fields_id=:fields_id");
     $query->bindValue(':fields_id', $field->id);
     $ft = new DatabaseQuerySelectFulltext($query);
     $ft->match(self::optionsTable, $property, $operator, $value);
     $result = $query->execute();
     $options = new SelectableOptionArray();
     $options->setField($field);
     while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
         $option = $this->arrayToOption($row);
         $options->add($option);
     }
     $options->resetTrackChanges();
     return $options;
 }
Ejemplo n.º 6
0
 /**
  * Builds LIMIT clause for the query
  */
 protected function _buildQueryLimit(DatabaseQuerySelect $query)
 {
     $limit = $this->getState()->limit;
     if ($limit) {
         $offset = $this->getState()->offset;
         $total = $this->getTotal();
         //If the offset is higher than the total recalculate the offset
         if ($offset !== 0 && $total !== 0) {
             if ($offset >= $total) {
                 $offset = floor(($total - 1) / $limit) * $limit;
                 $this->getState()->offset = $offset;
             }
         }
         $query->limit($limit, $offset);
     }
 }
 /**
  * Match a number of children count
  *
  */
 protected function getQueryNumChildren(DatabaseQuerySelect $query, $selector)
 {
     if (!in_array($selector->operator, array('=', '<', '>', '<=', '>=', '!='))) {
         throw new PageFinderSyntaxException("Operator '{$selector->operator}' not allowed for 'num_children' selector.");
     }
     $value = (int) $selector->value;
     $this->getQueryNumChildren++;
     $n = (int) $this->getQueryNumChildren;
     $a = "pages_num_children{$n}";
     $b = "num_children{$n}";
     if (in_array($selector->operator, array('<', '<=', '!=')) && $value || in_array($selector->operator, array('>', '>=', '!=')) && $value < 0 || ($selector->operator == '=' || $selector->operator == '>=') && !$value) {
         // allow for zero values
         $query->select("COUNT({$a}.id) AS {$b}");
         $query->leftjoin("pages AS {$a} ON ({$a}.parent_id=pages.id)");
         $query->groupby("HAVING COUNT({$a}.id){$selector->operator}{$value}");
         /* FOR REFERENCE
         			$query->select("count(pages_num_children$n.id) AS num_children$n"); 
         			$query->leftjoin("pages AS pages_num_children$n ON (pages_num_children$n.parent_id=pages.id)");
         			$query->groupby("HAVING count(pages_num_children$n.id){$selector->operator}$value"); 
         			*/
         return $b;
     } else {
         // non zero values
         $query->select("{$a}.{$b} AS {$b}");
         $query->leftjoin("(" . "SELECT p{$n}.parent_id, COUNT(p{$n}.id) AS {$b} " . "FROM pages AS p{$n} " . "GROUP BY p{$n}.parent_id " . "HAVING {$b}{$selector->operator}{$value} " . ") {$a} ON {$a}.parent_id=pages.id");
         $where = "{$a}.{$b}{$selector->operator}{$value}";
         $query->where($where);
         /* FOR REFERENCE
         			$query->select("pages_num_children$n.num_children$n AS num_children$n"); 
         			$query->leftjoin(
         				"(" . 
         				"SELECT p$n.parent_id, count(p$n.id) AS num_children$n " . 
         				"FROM pages AS p$n " . 
         				"GROUP BY p$n.parent_id " . 
         				"HAVING num_children$n{$selector->operator}$value" . 
         				") pages_num_children$n ON pages_num_children$n.parent_id=pages.id"); 
         
         			$query->where("pages_num_children$n.num_children$n{$selector->operator}$value");
         			*/
         return "{$a}.{$b}";
     }
 }
Ejemplo n.º 8
0
 /**
  * Given an array or CSV string of Page IDs, return a PageArray 
  *
  * Optionally specify an $options array rather than a template for argument 2. When present, the 'template' and 'parent_id' arguments may be provided
  * in the given $options array. These options may be specified: 
  * 
  * LOAD OPTIONS (argument 2 array): 
  * - cache: boolean, default=true. place loaded pages in memory cache?
  * - getFromCache: boolean, default=true. Allow use of previously cached pages in memory (rather than re-loading it from DB)?
  * - template: instance of Template (see $template argument)
  * - parent_id: integer (see $parent_id argument)
  * - getNumChildren: boolean, default=true. Specify false to disable retrieval and population of 'numChildren' Page property. 
  * - getOne: boolean, default=false. Specify true to return just one Page object, rather than a PageArray.
  * - autojoin: boolean, default=true. Allow use of autojoin option?
  * - joinFields: array, default=empty. Autojoin the field names specified in this array, regardless of field settings (requires autojoin=true).
  * - joinSortfield: boolean, default=true. Whether the 'sortfield' property will be joined to the page.
  * - findTemplates: boolean, default=true. Determine which templates will be used (when no template specified) for more specific autojoins.
  * - pageClass: string, default=auto-detect. Class to instantiate Page objects with. Leave blank to determine from template. 
  * - pageArrayClass: string, default=PageArray. PageArray-derived class to store pages in (when 'getOne' is false). 
  * 
  * Use the $options array for potential speed optimizations:
  * - Specify a 'template' with your call, when possible, so that this method doesn't have to determine it separately. 
  * - Specify false for 'getNumChildren' for potential speed optimization when you know for certain pages will not have children. 
  * - Specify false for 'autojoin' for potential speed optimization in certain scenarios (can also be a bottleneck, so be sure to test). 
  * - Specify false for 'joinSortfield' for potential speed optimization when you know the Page will not have children or won't need to know the order.
  * - Specify false for 'findTemplates' so this method doesn't have to look them up. Potential speed optimization if you have few autojoin fields globally.
  * - Note that if you specify false for 'findTemplates' the pageClass is assumed to be 'Page' unless you specify something different for the 'pageClass' option.
  *
  * @param array|WireArray|string $_ids Array of IDs or CSV string of IDs
  * @param Template|array|null $template Specify a template to make the load faster, because it won't have to attempt to join all possible fields... just those used by the template. 
  *	Optionally specify an $options array instead, see the method notes above. 
  * @param int|null $parent_id Specify a parent to make the load faster, as it reduces the possibility for full table scans. 
  *	This argument is ignored when an options array is supplied for the $template. 
  * @return PageArray|Page Returns Page only if the 'getOne' option is specified, otherwise always returns a PageArray.
  * @throws WireException
  *
  */
 public function getById($_ids, $template = null, $parent_id = null)
 {
     static $instanceID = 0;
     $options = array('cache' => true, 'getFromCache' => true, 'template' => null, 'parent_id' => null, 'getNumChildren' => true, 'getOne' => false, 'autojoin' => true, 'findTemplates' => true, 'joinSortfield' => true, 'joinFields' => array(), 'pageClass' => '', 'pageArrayClass' => 'PageArray');
     if (is_array($template)) {
         // $template property specifies an array of options
         $options = array_merge($options, $template);
         $template = $options['template'];
         $parent_id = $options['parent_id'];
     } else {
         if (!is_null($template) && !$template instanceof Template) {
             throw new WireException('getById argument 2 must be Template or $options array');
         }
     }
     $pageArrayClass = $options['pageArrayClass'];
     if (!is_null($parent_id) && !is_int($parent_id)) {
         // convert Page object or string to integer id
         $parent_id = (int) (string) $parent_id;
     }
     if (!is_null($template) && !is_object($template)) {
         // convert template string or id to Template object
         $template = $this->wire('templates')->get($template);
     }
     if (is_string($_ids)) {
         // convert string of IDs to array
         if (strpos($_ids, '|') !== false) {
             $_ids = explode('|', $_ids);
         } else {
             $_ids = explode(",", $_ids);
         }
     } else {
         if (is_int($_ids)) {
             $_ids = array($_ids);
         }
     }
     if (!WireArray::iterable($_ids) || !count($_ids)) {
         // return blank if $_ids isn't iterable or is empty
         return $options['getOne'] ? new NullPage() : new $pageArrayClass();
     }
     if (is_object($_ids)) {
         $_ids = $_ids->getArray();
     }
     // ArrayObject or the like
     $loaded = array();
     // array of id => Page objects that have been loaded
     $ids = array();
     // sanitized version of $_ids
     // sanitize ids and determine which pages we can pull from cache
     foreach ($_ids as $key => $id) {
         $id = (int) $id;
         if ($id < 1) {
             continue;
         }
         if ($options['getFromCache'] && ($page = $this->getCache($id))) {
             // page is already available in the cache
             $loaded[$id] = $page;
         } else {
             if (isset(Page::$loadingStack[$id])) {
                 // if the page is already in the process of being loaded, point to it rather than attempting to load again.
                 // the point of this is to avoid a possible infinite loop with autojoin fields referencing each other.
                 $loaded[$id] = Page::$loadingStack[$id];
                 // cache the pre-loaded version so that other pages referencing it point to this instance rather than loading again
                 $this->cache($loaded[$id]);
             } else {
                 $loaded[$id] = '';
                 // reserve the spot, in this order
                 $ids[(int) $key] = $id;
                 // queue id to be loaded
             }
         }
     }
     $idCnt = count($ids);
     // idCnt contains quantity of remaining page ids to load
     if (!$idCnt) {
         // if there are no more pages left to load, we can return what we've got
         if ($options['getOne']) {
             return count($loaded) ? reset($loaded) : new NullPage();
         }
         $pages = new $pageArrayClass();
         $pages->import($loaded);
         return $pages;
     }
     $database = $this->wire('database');
     $idsByTemplate = array();
     if (is_null($template) && $options['findTemplates']) {
         // template was not defined with the function call, so we determine
         // which templates are used by each of the pages we have to load
         $sql = "SELECT id, templates_id FROM pages WHERE ";
         if ($idCnt == 1) {
             $sql .= "id=" . (int) reset($ids);
         } else {
             $sql .= "id IN(" . implode(",", $ids) . ")";
         }
         $query = $database->prepare($sql);
         $result = $this->executeQuery($query);
         if ($result) {
             while ($row = $query->fetch(PDO::FETCH_NUM)) {
                 list($id, $templates_id) = $row;
                 $id = (int) $id;
                 $templates_id = (int) $templates_id;
                 if (!isset($idsByTemplate[$templates_id])) {
                     $idsByTemplate[$templates_id] = array();
                 }
                 $idsByTemplate[$templates_id][] = $id;
             }
         }
         $query->closeCursor();
     } else {
         if (is_null($template)) {
             // no template provided, and autojoin not needed (so we don't need to know template)
             $idsByTemplate = array(0 => $ids);
         } else {
             // template was provided
             $idsByTemplate = array($template->id => $ids);
         }
     }
     foreach ($idsByTemplate as $templates_id => $ids) {
         if ($templates_id && (!$template || $template->id != $templates_id)) {
             $template = $this->wire('templates')->get($templates_id);
         }
         if ($template) {
             $fields = $template->fieldgroup;
         } else {
             $fields = $this->wire('fields');
         }
         $query = new DatabaseQuerySelect();
         $sortfield = $template ? $template->sortfield : '';
         $joinSortfield = empty($sortfield) && $options['joinSortfield'];
         $query->select("false AS isLoaded, pages.templates_id AS templates_id, pages.*, " . ($joinSortfield ? 'pages_sortfields.sortfield, ' : '') . ($options['getNumChildren'] ? '(SELECT COUNT(*) FROM pages AS children WHERE children.parent_id=pages.id) AS numChildren' : ''));
         if ($joinSortfield) {
             $query->leftjoin('pages_sortfields ON pages_sortfields.pages_id=pages.id');
         }
         $query->groupby('pages.id');
         if ($options['autojoin'] && $this->autojoin) {
             foreach ($fields as $field) {
                 if (!empty($options['joinFields']) && in_array($field->name, $options['joinFields'])) {
                     // joinFields option specified to force autojoin this field
                 } else {
                     if (!($field->flags & Field::flagAutojoin)) {
                         continue;
                     }
                     // autojoin not enabled for field
                     if ($fields instanceof Fields && !($field->flags & Field::flagGlobal)) {
                         continue;
                     }
                     // non-fieldgroup, autojoin only if global flag is set
                 }
                 $table = $database->escapeTable($field->table);
                 if (!$field->type || !$field->type->getLoadQueryAutojoin($field, $query)) {
                     continue;
                 }
                 // autojoin not allowed
                 $query->leftjoin("{$table} ON {$table}.pages_id=pages.id");
                 // QA
             }
         }
         if (!is_null($parent_id)) {
             $query->where("pages.parent_id=" . (int) $parent_id);
         }
         if ($template) {
             $query->where("pages.templates_id=" . (int) $template->id);
         }
         // QA
         $query->where("pages.id IN(" . implode(',', $ids) . ") ");
         // QA
         $query->from("pages");
         $stmt = $query->prepare();
         $this->executeQuery($stmt);
         $class = $options['pageClass'];
         if (empty($class)) {
             if ($template) {
                 $class = $template->pageClass && class_exists($template->pageClass) ? $template->pageClass : 'Page';
             } else {
                 $class = 'Page';
             }
         }
         if ($class != 'Page' && !class_exists($class)) {
             $this->error("Class '{$class}' for Pages::getById() does not exist", Notice::log);
             $class = 'Page';
         }
         try {
             while ($page = $stmt->fetchObject($class, array($template))) {
                 $page->instanceID = ++$instanceID;
                 $page->setIsLoaded(true);
                 $page->setIsNew(false);
                 $page->setTrackChanges(true);
                 $page->setOutputFormatting($this->outputFormatting);
                 $loaded[$page->id] = $page;
                 if ($options['cache']) {
                     $this->cache($page);
                 }
             }
         } catch (Exception $e) {
             $error = $e->getMessage() . " [pageClass={$class}, template={$template}]";
             if ($this->wire('user')->isSuperuser()) {
                 $this->error($error);
             }
             $this->wire('log')->error($error);
             $this->trackException($e, false);
         }
         $stmt->closeCursor();
         $template = null;
     }
     if ($options['getOne']) {
         return count($loaded) ? reset($loaded) : new NullPage();
     }
     $pages = new $pageArrayClass();
     return $pages->import($loaded);
 }
Ejemplo n.º 9
0
 /**
  * Array Columns
  *
  * @return mixed
  *
  * @since 1.0.0
  */
 public function fetchColumn($column = 0)
 {
     $sth = $this->db->prepare($this->query);
     $sth->execute($this->query->getParameters());
     return $sth->fetchAll(\PDO::FETCH_COLUMN, $column);
 }
Ejemplo n.º 10
0
 /**
  * Given an array or CSV string of Page IDs, return a PageArray 
  *
  * @param array|WireArray|string $ids Array of IDs or CSV string of IDs
  * @param Template $template Specify a template to make the load faster, because it won't have to attempt to join all possible fields... just those used by the template. 
  * @param int $parent_id Specify a parent to make the load faster, as it reduces the possibility for full table scans
  * @return PageArray
  *
  */
 public function getById($ids, Template $template = null, $parent_id = null)
 {
     static $instanceID = 0;
     $pages = new PageArray();
     if (is_string($ids)) {
         $ids = explode(",", $ids);
     }
     if (!WireArray::iterable($ids) || !count($ids)) {
         return $pages;
     }
     if (is_object($ids)) {
         $ids = $ids->getArray();
     }
     $loaded = array();
     foreach ($ids as $key => $id) {
         $id = (int) $id;
         $ids[$key] = $id;
         if ($page = $this->getCache($id)) {
             $loaded[$id] = $page;
             unset($ids[$key]);
         } else {
             if (isset(Page::$loadingStack[$id])) {
                 // if the page is already in the process of being loaded, point to it rather than attempting to load again.
                 // the point of this is to avoid a possible infinite loop with autojoin fields referencing each other.
                 $loaded[$id] = Page::$loadingStack[$id];
                 // cache the pre-loaded version so that other pages referencing it point to this instance rather than loading again
                 $this->cache($loaded[$id]);
                 unset($ids[$key]);
             } else {
                 $loaded[$id] = '';
                 // reserve the spot, in this order
             }
         }
     }
     $idCnt = count($ids);
     if (!$idCnt) {
         return $pages->import($loaded);
     }
     $idsByTemplate = array();
     if (is_null($template)) {
         $sql = "SELECT id, templates_id FROM pages WHERE ";
         if ($idCnt == 1) {
             $sql .= "id=" . (int) reset($ids);
         } else {
             $sql .= "id IN(" . implode(",", $ids) . ")";
         }
         $result = $this->db->query($sql);
         if ($result && $result->num_rows) {
             while ($row = $result->fetch_row()) {
                 list($id, $templates_id) = $row;
                 if (!isset($idsByTemplate[$templates_id])) {
                     $idsByTemplate[$templates_id] = array();
                 }
                 $idsByTemplate[$templates_id][] = $id;
             }
         }
         $result->free();
     } else {
         $idsByTemplate = array($template->id => $ids);
     }
     foreach ($idsByTemplate as $templates_id => $ids) {
         if (!$template || $template->id != $templates_id) {
             $template = $this->fuel('templates')->get($templates_id);
         }
         $fields = $template->fieldgroup;
         $query = new DatabaseQuerySelect();
         $query->select("false AS isLoaded, pages.templates_id AS templates_id, pages.*, pages_sortfields.sortfield, " . "(SELECT COUNT(*) FROM pages AS children WHERE children.parent_id=pages.id) AS numChildren");
         $query->leftjoin("pages_sortfields ON pages_sortfields.pages_id=pages.id");
         $query->groupby("pages.id");
         foreach ($fields as $field) {
             if (!($field->flags & Field::flagAutojoin)) {
                 continue;
             }
             $table = $field->table;
             if (!$field->type->getLoadQueryAutojoin($field, $query)) {
                 continue;
             }
             // autojoin not allowed
             $query->leftjoin("{$table} ON {$table}.pages_id=pages.id");
         }
         if (!is_null($parent_id)) {
             $query->where("pages.parent_id=" . (int) $parent_id);
         }
         $query->where("pages.templates_id={$template->id}");
         $query->where("pages.id IN(" . implode(',', $ids) . ") ");
         $query->from("pages");
         if (!($result = $query->execute())) {
             throw new WireException($this->db->error);
         }
         $class = $template->pageClass && class_exists($template->pageClass) ? $template->pageClass : 'Page';
         while ($page = $result->fetch_object($class, array($template))) {
             $page->instanceID = ++$instanceID;
             $page->setIsLoaded(true);
             $page->setIsNew(false);
             $page->setTrackChanges(true);
             $page->setOutputFormatting($this->outputFormatting);
             $loaded[$page->id] = $page;
             $this->cache($page);
         }
         $template = null;
         $result->free();
     }
     return $pages->import($loaded);
 }
Ejemplo n.º 11
0
 /**
  * Make the query specific to all pages below a certain parent (children, grandchildren, great grandchildren, etc.)
  *
  */
 protected function getQueryHasParent(DatabaseQuerySelect $query, $selector)
 {
     $parent_id = (int) $selector->value;
     $query->join("pages_parents ON (" . "pages_parents.pages_id=pages.parent_id " . "AND (" . "pages_parents.parents_id={$parent_id} " . "OR pages_parents.pages_id={$parent_id} " . ")" . ")");
 }
Ejemplo n.º 12
0
 /**
  * Given an array or CSV string of Page IDs, return a PageArray 
  *
  * Optionally specify an $options array rather than a template for argument 2. When present, the 'template' and 'parent_id' arguments may be provided
  * in the given $options array. These options may be specified: 
  * 
  * - template: instance of Template (see $template argument)
  * - parent_id: integer (see $parent_id argument)
  * - getNumChildren: boolean, default=true. Specify false to disable retrieval and population of 'numChildren' Page property. 
  *
  * @param array|WireArray|string $ids Array of IDs or CSV string of IDs
  * @param Template|array|null $template Specify a template to make the load faster, because it won't have to attempt to join all possible fields... just those used by the template. 
  *	Optionally specify an $options array instead, see the method notes above. 
  * @param int|null $parent_id Specify a parent to make the load faster, as it reduces the possibility for full table scans. 
  *	This argument is ignored when an options array is supplied for the $template. 
  * @return PageArray
  * @throws WireException
  *
  */
 public function getById($ids, $template = null, $parent_id = null)
 {
     $options = array('template' => null, 'parent_id' => null, 'getNumChildren' => true);
     if (is_array($template)) {
         // $template property specifies an array of options
         $options = array_merge($options, $template);
         $template = $options['template'];
         $parent_id = $options['parent_id'];
     } else {
         if (!is_null($template) && !$template instanceof Template) {
             throw new WireException('getById argument 2 must be Template or $options array');
         }
     }
     static $instanceID = 0;
     $database = $this->wire('database');
     $pages = new PageArray();
     if (is_string($ids)) {
         $ids = explode(",", $ids);
     }
     if (!WireArray::iterable($ids) || !count($ids)) {
         return $pages;
     }
     if (is_object($ids)) {
         $ids = $ids->getArray();
     }
     $loaded = array();
     foreach ($ids as $key => $id) {
         $id = (int) $id;
         $ids[$key] = $id;
         if ($page = $this->getCache($id)) {
             $loaded[$id] = $page;
             unset($ids[$key]);
         } else {
             if (isset(Page::$loadingStack[$id])) {
                 // if the page is already in the process of being loaded, point to it rather than attempting to load again.
                 // the point of this is to avoid a possible infinite loop with autojoin fields referencing each other.
                 $loaded[$id] = Page::$loadingStack[$id];
                 // cache the pre-loaded version so that other pages referencing it point to this instance rather than loading again
                 $this->cache($loaded[$id]);
                 unset($ids[$key]);
             } else {
                 $loaded[$id] = '';
                 // reserve the spot, in this order
             }
         }
     }
     $idCnt = count($ids);
     if (!$idCnt) {
         return $pages->import($loaded);
     }
     $idsByTemplate = array();
     if (is_null($template)) {
         $sql = "SELECT id, templates_id FROM pages WHERE ";
         if ($idCnt == 1) {
             $sql .= "id=" . (int) reset($ids);
         } else {
             $sql .= "id IN(" . implode(",", $ids) . ")";
         }
         $query = $database->prepare($sql);
         $result = $query->execute();
         if ($result) {
             while ($row = $query->fetch(PDO::FETCH_NUM)) {
                 list($id, $templates_id) = $row;
                 if (!isset($idsByTemplate[$templates_id])) {
                     $idsByTemplate[$templates_id] = array();
                 }
                 $idsByTemplate[$templates_id][] = $id;
             }
         }
         $query->closeCursor();
     } else {
         $idsByTemplate = array($template->id => $ids);
     }
     foreach ($idsByTemplate as $templates_id => $ids) {
         if (!$template || $template->id != $templates_id) {
             $template = $this->wire('templates')->get($templates_id);
         }
         $fields = $template->fieldgroup;
         $query = new DatabaseQuerySelect();
         $joinSortfield = empty($template->sortfield);
         $query->select("false AS isLoaded, pages.templates_id AS templates_id, pages.*, " . ($joinSortfield ? 'pages_sortfields.sortfield, ' : '') . ($options['getNumChildren'] ? '(SELECT COUNT(*) FROM pages AS children WHERE children.parent_id=pages.id) AS numChildren' : ''));
         if ($joinSortfield) {
             $query->leftjoin('pages_sortfields ON pages_sortfields.pages_id=pages.id');
         }
         $query->groupby('pages.id');
         foreach ($fields as $field) {
             if (!($field->flags & Field::flagAutojoin)) {
                 continue;
             }
             $table = $database->escapeTable($field->table);
             if (!$field->type || !$field->type->getLoadQueryAutojoin($field, $query)) {
                 continue;
             }
             // autojoin not allowed
             $query->leftjoin("{$table} ON {$table}.pages_id=pages.id");
             // QA
         }
         if (!is_null($parent_id)) {
             $query->where("pages.parent_id=" . (int) $parent_id);
         }
         $query->where("pages.templates_id=" . (int) $template->id);
         // QA
         $query->where("pages.id IN(" . implode(',', $ids) . ") ");
         // QA
         $query->from("pages");
         $stmt = $query->execute();
         if ($stmt->errorCode() > 0) {
             $errorInfo = $result->errorInfo();
             throw new WireException($errorInfo[2]);
         }
         $class = $template->pageClass && class_exists($template->pageClass) ? $template->pageClass : 'Page';
         while ($page = $stmt->fetchObject($class, array($template))) {
             $page->instanceID = ++$instanceID;
             $page->setIsLoaded(true);
             $page->setIsNew(false);
             $page->setTrackChanges(true);
             $page->setOutputFormatting($this->outputFormatting);
             $loaded[$page->id] = $page;
             $this->cache($page);
         }
         $stmt->closeCursor();
         $template = null;
     }
     return $pages->import($loaded);
 }
 /**
  * Return the query used for Autojoining this field (if different from getLoadQuery) or NULL if autojoin not allowed. 
  *
  * @param Field $field
  * @param DatabaseQuerySelect $query
  * @return DatabaseQuerySelect|NULL
  *
  */
 public function getLoadQueryAutojoin(Field $field, DatabaseQuerySelect $query)
 {
     $table = $field->table;
     $schema = $this->trimDatabaseSchema($this->getDatabaseSchema($field));
     $fieldName = $field->name;
     $separator = self::multiValueSeparator;
     foreach ($schema as $key => $unused) {
         $query->select("GROUP_CONCAT({$table}.{$key} SEPARATOR '{$separator}') AS `{$fieldName}__{$key}`");
     }
     return $query;
 }
Ejemplo n.º 14
0
 /**
  * Given an array or CSV string of Page IDs, return a PageArray 
  *
  * @param array|WireArray|string $ids Array of IDs or CSV string of IDs
  * @param Template $template Specify a template to make the load faster, because it won't have to attempt to join all possible fields... just those used by the template. 
  * @param int $parent_id Specify a parent to make the load faster, as it reduces the possibility for full table scans
  * @return PageArray
  *
  */
 public function getById($ids, Template $template = null, $parent_id = null)
 {
     static $instanceID = 0;
     $pages = new PageArray();
     if (is_string($ids)) {
         $ids = explode(",", $ids);
     }
     if (!WireArray::iterable($ids) || !count($ids)) {
         return $pages;
     }
     if (is_object($ids)) {
         $ids = $ids->getArray();
     }
     $loaded = array();
     foreach ($ids as $key => $id) {
         $id = (int) $id;
         $ids[$key] = $id;
         if ($page = $this->getCache($id)) {
             $loaded[$id] = $page;
             unset($ids[$key]);
         } else {
             $loaded[$id] = '';
             // reserve the spot, in this order
         }
     }
     $idCnt = count($ids);
     $fields = is_null($template) ? $this->fuel->fields : $template->fieldgroup;
     if ($idCnt) {
         // Optimization to only load the fields specific to the page, if just one page.
         // Without it, all autojoin fields have to be attempted, whether they are applicable to the pages loaded or not.
         // Even though this increases queries, it does result in a slight overall speed and memory improvement.
         if (is_null($template) && $idCnt == 1) {
             $result = $this->db->query("SELECT templates_id FROM pages WHERE id=" . (int) reset($ids));
             if ($result) {
                 list($tpl_id) = $result->fetch_row();
                 $template = $this->fuel('templates')->get($tpl_id);
                 if ($template) {
                     $fields = $template->fieldgroup;
                 }
                 $result->free();
             }
         }
         $query = new DatabaseQuerySelect();
         $query->select("false AS isLoaded, pages.templates_id AS templates_id, pages.*, pages_sortfields.sortfield, " . "(SELECT COUNT(*) FROM pages AS children WHERE children.parent_id=pages.id) AS numChildren");
         $query->leftjoin("pages_sortfields ON pages_sortfields.pages_id=pages.id");
         $query->groupby("pages.id");
         foreach ($fields as $field) {
             if (!($field->flags & Field::flagAutojoin)) {
                 continue;
             }
             //if($field->type instanceof FieldtypeMulti) continue;
             $table = $field->table;
             // if($field->type instanceof FieldtypeMulti) {
             // 	$sel .= "(SELECT COUNT(*) FROM $table WHERE $table.pages_id=pages.id) AS {$field->name}, ";
             // } else {
             if (!$field->type->getLoadQueryAutojoin($field, $query)) {
                 continue;
             }
             // autojoin not allowed
             // $query->select("$table.data AS {$field->name}");
             $query->leftjoin("{$table} ON {$table}.pages_id=pages.id");
         }
         if (!is_null($parent_id)) {
             $query->where("pages.parent_id=" . (int) $parent_id);
         }
         if (!is_null($template)) {
             $query->where("pages.templates_id={$template->id}");
         }
         $query->where("pages.id IN(" . implode(',', $ids) . ") ");
         $query->from("pages");
         if (!($result = $query->execute())) {
             throw new WireException($this->db->error);
         }
         while ($page = $result->fetch_object('Page', array($template))) {
             $page->instanceID = ++$instanceID;
             $page->setIsLoaded(true);
             $page->setIsNew(false);
             $page->setTrackChanges(true);
             $page->setOutputFormatting($this->outputFormatting);
             $loaded[$page->id] = $page;
             $this->cache($page);
         }
         $result->free();
     }
     $pages->import($loaded);
     return $pages;
 }