mirror of
https://github.com/processwire/processwire.git
synced 2025-08-08 07:47:00 +02:00
Add a new $pages->findIDs() method, plus some experimental options in PageFinder
This commit is contained in:
@@ -130,6 +130,18 @@ class PageFinder extends Wire {
|
||||
*/
|
||||
'allowCustom' => false,
|
||||
|
||||
/**
|
||||
* Use sortsAfter feature where PageFinder lets you perform the sorting manually after the find()
|
||||
*
|
||||
* When in use, you can access the PageFinder::getSortsAfter() method to retrieve an array of sort
|
||||
* fields that should be sent to PageArray::sort()
|
||||
*
|
||||
* So far this option seems to add more overhead in most cases (rather than save it) so recommend not
|
||||
* using it. Kept for further experimenting.
|
||||
*
|
||||
*/
|
||||
'useSortsAfter' => false,
|
||||
|
||||
);
|
||||
|
||||
protected $fieldgroups;
|
||||
@@ -144,6 +156,7 @@ class PageFinder extends Wire {
|
||||
protected $getQueryNumChildren = 0; // number of times the function has been called
|
||||
protected $lastOptions = array();
|
||||
protected $extraOrSelectors = array(); // one from each field must match
|
||||
protected $sortsAfter = array(); // apply these sorts after pages loaded
|
||||
|
||||
// protected $extraSubSelectors = array(); // subselectors that are added in after getQuery()
|
||||
// protected $extraJoins = array();
|
||||
@@ -342,6 +355,7 @@ class PageFinder extends Wire {
|
||||
* template ID and score. When false, returns only an array of page IDs. True is required by most usage from
|
||||
* Pages class. False is only for specific cases.
|
||||
* - `allowCustom` (bool): Whether or not to allow _custom='selector string' type values (default=false).
|
||||
* - `useSortsAfter` (bool): When true, PageFinder may ask caller to perform sort manually in some cases (default=false).
|
||||
* @return array|DatabaseQuerySelect
|
||||
* @throws PageFinderException
|
||||
*
|
||||
@@ -357,8 +371,6 @@ class PageFinder extends Wire {
|
||||
$this->fieldgroups = $this->wire('fieldgroups');
|
||||
$options = array_merge($this->defaultOptions, $options);
|
||||
|
||||
$this->start = 0; // reset for new find operation
|
||||
$this->limit = 0;
|
||||
$this->parent_id = null;
|
||||
$this->templates_id = null;
|
||||
$this->checkAccess = true;
|
||||
@@ -490,16 +502,62 @@ class PageFinder extends Wire {
|
||||
*
|
||||
*/
|
||||
protected function preProcessSelectors(Selectors $selectors, $options = array()) {
|
||||
if(!empty($options['allowCustom'])) {
|
||||
foreach($selectors as $selector) {
|
||||
$field = $selector->field;
|
||||
if(!is_string($field) || $field !== '_custom') continue;
|
||||
|
||||
$sortSelectors = array();
|
||||
$start = null;
|
||||
$limit = null;
|
||||
|
||||
foreach($selectors as $selector) {
|
||||
$field = $selector->field;
|
||||
|
||||
if($field === '_custom') {
|
||||
$selectors->remove($selector);
|
||||
$_selectors = $this->wire(new Selectors($selector->value()));
|
||||
/** @var Selectors $_selectors */
|
||||
foreach($_selectors as $s) $selectors->add($s);
|
||||
if(!empty($options['allowCustom'])) {
|
||||
$_selectors = $this->wire(new Selectors($selector->value()));
|
||||
/** @var Selectors $_selectors */
|
||||
foreach($_selectors as $s) $selectors->add($s);
|
||||
}
|
||||
|
||||
} else if($field === 'sort') {
|
||||
if(!empty($options['useSortsAfter']) && $selector->operator == '=' && strpos($selector->value, '.') === false) {
|
||||
$sortSelectors[] = $selector;
|
||||
}
|
||||
|
||||
} else if($field === 'limit') {
|
||||
$limit = (int) $selector->value;
|
||||
|
||||
} else if($field === 'start') {
|
||||
$start = (int) $selector->value;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$limit && !$start && count($sortSelectors)
|
||||
&& $options['returnVerbose'] && !empty($options['useSortsAfter'])
|
||||
&& empty($options['startAfterID']) && empty($options['stopBeforeID'])) {
|
||||
// the `useSortsAfter` option is enabled and potentially applicable
|
||||
$sortsAfter = array();
|
||||
foreach($sortSelectors as $n => $selector) {
|
||||
if(!$n && $this->wire('pages')->loader()->isNativeColumn($selector->value)) {
|
||||
// first iteration only, see if it's a native column and prevent sortsAfter if so
|
||||
break;
|
||||
}
|
||||
if(strpos($selector->value, '.') !== false) {
|
||||
// we don't supports sortsAfter for subfields, so abandon entirely
|
||||
$sortsAfter = array();
|
||||
break;
|
||||
}
|
||||
if($selector->operator != '=') {
|
||||
// sort property being used for something else that we don't recognize
|
||||
continue;
|
||||
}
|
||||
$sortsAfter[] = $selector->value;
|
||||
$selectors->remove($selector);
|
||||
}
|
||||
$this->sortsAfter = $sortsAfter;
|
||||
}
|
||||
|
||||
$this->limit = $limit;
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
|
||||
@@ -802,8 +860,8 @@ class PageFinder extends Wire {
|
||||
if(count($fields) > 1) $fields = $this->arrangeFields($fields);
|
||||
$fieldsStr = ':' . implode(':', $fields) . ':'; // for strpos
|
||||
$field = reset($fields); // first field
|
||||
$subfield = '';
|
||||
if(strpos($field, '.')) list($field, $subfield) = explode('.', $field);
|
||||
else $subfield = '';
|
||||
|
||||
// TODO Make native fields and path/url multi-field and multi-value aware
|
||||
if($field == 'sort' && $selector->operator === '=' && !$subfield) {
|
||||
@@ -811,7 +869,7 @@ class PageFinder extends Wire {
|
||||
continue;
|
||||
|
||||
} else if($field == 'limit' || $field == 'start') {
|
||||
if(!$startLimit) $this->getQueryStartLimit($query, $selectors);
|
||||
if(!$startLimit) $this->getQueryStartLimit($query);
|
||||
$startLimit = true;
|
||||
continue;
|
||||
|
||||
@@ -1488,20 +1546,13 @@ class PageFinder extends Wire {
|
||||
}
|
||||
}
|
||||
|
||||
protected function getQueryStartLimit(DatabaseQuerySelect $query, $selectors) {
|
||||
protected function getQueryStartLimit(DatabaseQuerySelect $query) {
|
||||
|
||||
$start = null;
|
||||
$limit = null;
|
||||
$sql = '';
|
||||
|
||||
foreach($selectors as $selector) {
|
||||
if($selector->field == 'start') $start = (int) $selector->value;
|
||||
else if($selector->field == 'limit') $limit = (int) $selector->value;
|
||||
}
|
||||
$start = $this->start;
|
||||
$limit = $this->limit;
|
||||
|
||||
if($limit) {
|
||||
|
||||
$this->limit = $limit;
|
||||
$sql = '';
|
||||
|
||||
if(is_null($start) && ($input = $this->wire('input'))) {
|
||||
// if not specified in the selector, assume the 'start' property from the default page's pageNum
|
||||
@@ -1511,15 +1562,13 @@ class PageFinder extends Wire {
|
||||
|
||||
if(!is_null($start)) {
|
||||
$sql .= "$start,";
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
$sql .= "$limit";
|
||||
|
||||
if($this->getTotal && $this->getTotalType != 'count') $query->select("SQL_CALC_FOUND_ROWS");
|
||||
if($this->getTotal && $this->getTotalType != 'count') $query->select("SQL_CALC_FOUND_ROWS");
|
||||
if($sql) $query->limit($sql);
|
||||
}
|
||||
|
||||
if($sql) $query->limit($sql);
|
||||
}
|
||||
|
||||
|
||||
@@ -1951,7 +2000,7 @@ class PageFinder extends Wire {
|
||||
*
|
||||
*/
|
||||
public function getLimit() {
|
||||
return $this->limit;
|
||||
return $this->limit === null ? 0 : $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1961,7 +2010,7 @@ class PageFinder extends Wire {
|
||||
*
|
||||
*/
|
||||
public function getStart() {
|
||||
return $this->start;
|
||||
return $this->start === null ? 0 : $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1994,6 +2043,20 @@ class PageFinder extends Wire {
|
||||
return $this->lastOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of sortfields that should be applied to resulting PageArray after loaded
|
||||
*
|
||||
* See the `useSortsAfter` option which must be enabled to use this.
|
||||
*
|
||||
* #pw-internal
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function getSortsAfter() {
|
||||
return $this->sortsAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given field or fieldName resolve to a field that uses Page or PageArray values?
|
||||
*
|
||||
|
@@ -298,6 +298,34 @@ class Pages extends Wire {
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like $pages->find() except returns array of IDs rather than Page objects.
|
||||
*
|
||||
* This is a faster method to use when you only need to know the matching page IDs.
|
||||
*
|
||||
* #pw-group-retrieval
|
||||
*
|
||||
* @param string|array|Selectors $selector Selector to find page IDs.
|
||||
* @param array|bool $options Options to modify behavior.
|
||||
* - `verbose` (bool): Specify true to make return value array of arrays with [ id, parent_id, templates_id ] for each page.
|
||||
* - The verbose option above can also be specified by providing boolean true as the $options argument.
|
||||
* - See `Pages::find()` $options argument for additional options.
|
||||
* @return array Array of page IDs, or in verbose mode: array of arrays with id, parent_id and templates_id.
|
||||
* @since 3.0.46
|
||||
*
|
||||
*/
|
||||
public function findIDs($selector, $options = array()) {
|
||||
$verbose = false;
|
||||
if($options === true) $verbose = true;
|
||||
if(!is_array($options)) $options = array();
|
||||
if(isset($options['verbose'])) {
|
||||
$verbose = $options['verbose'];
|
||||
unset($options['verbose']);
|
||||
}
|
||||
$options['findIDs'] = $verbose ? true : 1;
|
||||
return $this->find($selector, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first page matching the given selector with no exclusions
|
||||
*
|
||||
|
@@ -173,7 +173,8 @@ class PagesLoader extends Wire {
|
||||
* @param string|int|array|Selectors $selector Specify selector (standard usage), but can also accept page ID or array of page IDs.
|
||||
* @param array|string $options Optional one or more options that can modify certain behaviors. May be assoc array or key=value string.
|
||||
* - findOne: boolean - apply optimizations for finding a single page
|
||||
* - findAll: boolean - find all pages with no exculsions (same as include=all option)
|
||||
* - findAll: boolean - find all pages with no exclusions (same as include=all option)
|
||||
* - findIDs: boolean|int - true=return array of [id, template_id, parent_id], or 1=return just page IDs.
|
||||
* - getTotal: boolean - whether to set returning PageArray's "total" property (default: true except when findOne=true)
|
||||
* - cache: boolean - Allow caching of selectors and pages loaded (default=true). Also sets loadOptions[cache].
|
||||
* - allowCustom: boolean - Whether to allow use of "_custom=new selector" in selectors (default=false).
|
||||
@@ -199,11 +200,12 @@ class PagesLoader extends Wire {
|
||||
$loadPages = array_key_exists('loadPages', $options) ? (bool) $options['loadPages'] : true;
|
||||
$caller = isset($options['caller']) ? $options['caller'] : 'pages.find';
|
||||
$lazy = empty($options['lazy']) ? false : true;
|
||||
$findIDs = isset($options['findIDs']) ? $options['findIDs'] : false;
|
||||
$debug = $this->debug && !$lazy;
|
||||
$cachePages = isset($options['cache']) ? (bool) $options['cache'] : true;
|
||||
if(!$cachePages && !isset($loadOptions['cache'])) $loadOptions['cache'] = false;
|
||||
|
||||
if($loadPages) {
|
||||
if($loadPages && !$lazy && !$findIDs) {
|
||||
$pages = $this->findShortcut($selector, $options, $loadOptions);
|
||||
if($pages) return $pages;
|
||||
}
|
||||
@@ -222,10 +224,12 @@ class PagesLoader extends Wire {
|
||||
$selectorString = is_string($selector) ? $selector : (string) $selectors;
|
||||
|
||||
// see if this has been cached and return it if so
|
||||
$pages = $this->pages->cacher()->getSelectorCache($selectorString, $options);
|
||||
if(!is_null($pages)) {
|
||||
if($debug) $this->pages->debugLog('find', $selectorString, $pages . ' [from-cache]');
|
||||
return $pages;
|
||||
if($loadPages && !$findIDs && !$lazy) {
|
||||
$pages = $this->pages->cacher()->getSelectorCache($selectorString, $options);
|
||||
if(!is_null($pages)) {
|
||||
if($debug) $this->pages->debugLog('find', $selectorString, $pages . ' [from-cache]');
|
||||
return $pages;
|
||||
}
|
||||
}
|
||||
|
||||
$pageFinder = $this->pages->getPageFinder();
|
||||
@@ -236,8 +240,9 @@ class PagesLoader extends Wire {
|
||||
$profiler = $this->wire('profiler');
|
||||
$profilerEvent = $profiler ? $profiler->start("$caller($selectorString)", "Pages") : null;
|
||||
|
||||
if($lazy) {
|
||||
if(strpos($selectorString, 'limit=') === false) $options['getTotal'] = false;
|
||||
if(($lazy || $findIDs) && strpos($selectorString, 'limit=') === false) $options['getTotal'] = false;
|
||||
|
||||
if($lazy || $findIDs === 1) {
|
||||
$pagesIDs = $pageFinder->findIDs($selectors, $options);
|
||||
} else {
|
||||
$pagesInfo = $pageFinder->find($selectors, $options);
|
||||
@@ -261,7 +266,7 @@ class PagesLoader extends Wire {
|
||||
$loadPages = false;
|
||||
$cachePages = false;
|
||||
$template = null;
|
||||
|
||||
|
||||
foreach($pagesIDs as $id) {
|
||||
$page = $this->pages->newPage();
|
||||
$page->_lazy($id);
|
||||
@@ -272,6 +277,12 @@ class PagesLoader extends Wire {
|
||||
$pages->setDuplicateChecking(true);
|
||||
if(count($pagesIDs)) $pages->_lazy(true);
|
||||
|
||||
} else if($findIDs) {
|
||||
|
||||
$loadPages = false;
|
||||
$cachePages = false;
|
||||
$pages = $this->pages->newPageArray($loadOptions); // only for hooks to see
|
||||
|
||||
} else if($loadPages) {
|
||||
// parent_id is null unless a single parent was specified in the selectors
|
||||
$parent_id = $pageFinder->getParentID();
|
||||
@@ -288,6 +299,7 @@ class PagesLoader extends Wire {
|
||||
|
||||
if(count($idsByTemplate) > 1) {
|
||||
// perform a load for each template, which results in unsorted pages
|
||||
// @todo use $idsUnsorted array rather than $unsortedPages PageArray
|
||||
$unsortedPages = $this->pages->newPageArray($loadOptions);
|
||||
foreach($idsByTemplate as $tpl_id => $ids) {
|
||||
$opt = $loadOptions;
|
||||
@@ -315,6 +327,9 @@ class PagesLoader extends Wire {
|
||||
$opt['parent_id'] = $parent_id;
|
||||
$pages->import($this->getById($idsSorted, $opt));
|
||||
}
|
||||
|
||||
$sortsAfter = $pageFinder->getSortsAfter();
|
||||
if(count($sortsAfter)) $pages->sort($sortsAfter);
|
||||
|
||||
} else {
|
||||
$pages = $this->pages->newPageArray($loadOptions);
|
||||
@@ -353,6 +368,8 @@ class PagesLoader extends Wire {
|
||||
'pagesInfo' => $pagesInfo,
|
||||
'options' => $options
|
||||
));
|
||||
|
||||
if($findIDs) return $findIDs === 1 ? $pagesIDs : $pagesInfo;
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
Reference in New Issue
Block a user