mirror of
https://github.com/processwire/processwire.git
synced 2025-08-09 16:26:59 +02:00
Add new find methods to FieldtypePage, FieldtypeTextarea and MarkupQA for finding page references and links specific to a given page. The public interfaces to these will be in the Page class (coming in next commit).
This commit is contained in:
@@ -415,6 +415,7 @@ class MarkupQA extends Wire {
|
|||||||
$languageID = 0;
|
$languageID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$pageID = (int) $pageID;
|
||||||
$full = $matches[0][$key];
|
$full = $matches[0][$key];
|
||||||
$start = $matches[1][$key];
|
$start = $matches[1][$key];
|
||||||
$href = $matches[3][$key];
|
$href = $matches[3][$key];
|
||||||
@@ -427,7 +428,7 @@ class MarkupQA extends Wire {
|
|||||||
$language = null;
|
$language = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$livePath = $this->wire('pages')->getPath((int) $pageID, array(
|
$livePath = $this->wire('pages')->getPath($pageID, array(
|
||||||
'language' => $language
|
'language' => $language
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -469,6 +470,108 @@ class MarkupQA extends Wire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find pages linking to another
|
||||||
|
*
|
||||||
|
* @param Page $page Page to find links to, or omit to use page specified in constructor
|
||||||
|
* @param array $fieldNames Field names to look in or omit to use field specified in constructor
|
||||||
|
* @param string $selector Optional selector to use as a filter
|
||||||
|
* @param array $options Additional options
|
||||||
|
* - `getIDs` (bool): Return array of page IDs rather than Page instances. (default=false)
|
||||||
|
* - `getCount` (bool): Return a total count (int) of found pages rather than Page instances. (default=false)
|
||||||
|
* - `confirm` (bool): Confirm that the links are present by looking at the actual page field data. (default=true)
|
||||||
|
* You can specify false for this option to make it perform faster, but with a potentially less accurate result.
|
||||||
|
* @return PageArray|array|int
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function findLinks(Page $page = null, $fieldNames = array(), $selector = '', array $options = array()) {
|
||||||
|
|
||||||
|
$defaults = array(
|
||||||
|
'getIDs' => false,
|
||||||
|
'getCount' => false,
|
||||||
|
'confirm' => true
|
||||||
|
);
|
||||||
|
|
||||||
|
$options = array_merge($defaults, $options);
|
||||||
|
|
||||||
|
if($options['getIDs']) {
|
||||||
|
$result = array();
|
||||||
|
} else if($options['getCount']) {
|
||||||
|
$result = 0;
|
||||||
|
} else {
|
||||||
|
$result = $this->wire('pages')->newPageArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$page) $page = $this->page;
|
||||||
|
if(!$page) return $result;
|
||||||
|
|
||||||
|
if(empty($fieldNames)) {
|
||||||
|
if($this->field) $fieldNames[] = $this->field->name;
|
||||||
|
if(empty($fieldNames)) return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($selector === true) $selector = "include=all";
|
||||||
|
$op = strlen("$page->id") > 3 ? "~=" : "%=";
|
||||||
|
$selector = implode('|', $fieldNames) . "$op'$page->id', id!=$page->id, $selector";
|
||||||
|
$selector = trim($selector, ', ');
|
||||||
|
|
||||||
|
|
||||||
|
// find pages
|
||||||
|
if($options['getCount'] && !$options['confirm']) {
|
||||||
|
// just return a count
|
||||||
|
return $this->wire('pages')->count($selector);
|
||||||
|
} else {
|
||||||
|
// find the IDs
|
||||||
|
$checkIDs = array();
|
||||||
|
$foundIDs = $this->wire('pages')->findIDs($selector);
|
||||||
|
if(!count($foundIDs)) return $result;
|
||||||
|
if($options['confirm']) {
|
||||||
|
$checkIDs = array_flip($foundIDs);
|
||||||
|
$foundIDs = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm results
|
||||||
|
foreach($fieldNames as $fieldName) {
|
||||||
|
if(!count($checkIDs)) break;
|
||||||
|
$field = $this->wire('fields')->get($fieldName);
|
||||||
|
if(!$field) continue;
|
||||||
|
$table = $field->getTable();
|
||||||
|
$ids = implode(',', array_keys($checkIDs));
|
||||||
|
$sql = "SELECT * FROM `$table` WHERE `pages_id` IN($ids)";
|
||||||
|
$query = $this->wire('database')->prepare($sql);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
while($row = $query->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
$pageID = (int) $row['pages_id'];
|
||||||
|
if(isset($foundIDs[$pageID])) continue;
|
||||||
|
$row = implode(' ', $row);
|
||||||
|
$find = "data-pwid=$page->id";
|
||||||
|
// first check if it might be there
|
||||||
|
if(!strpos($row, $find)) continue;
|
||||||
|
// then confirm with a more accurate check
|
||||||
|
if(!strpos($row, "$find ") && !strpos($row, "$find\t") && !strpos($row, "$find-")) continue;
|
||||||
|
// at this point we have confirmed that this item links to $page
|
||||||
|
unset($checkIDs[$pageID]);
|
||||||
|
$foundIDs[$pageID] = $pageID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->closeCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($foundIDs)) {
|
||||||
|
if($options['getIDs']) {
|
||||||
|
$result = $foundIDs;
|
||||||
|
} else if($options['getCount']) {
|
||||||
|
$result = count($foundIDs);
|
||||||
|
} else {
|
||||||
|
$result = $this->wire('pages')->getById($foundIDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display and log a warning about a path that didn't resolve
|
* Display and log a warning about a path that didn't resolve
|
||||||
*
|
*
|
||||||
@@ -554,7 +657,7 @@ class MarkupQA extends Wire {
|
|||||||
list($name, $val) = explode('=', $attr);
|
list($name, $val) = explode('=', $attr);
|
||||||
|
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
$val = trim($val, "\"' ");
|
$val = trim($val, "\"'> ");
|
||||||
|
|
||||||
if($name == 'alt' && !strlen($val)) {
|
if($name == 'alt' && !strlen($val)) {
|
||||||
$replaceAlt = $attr;
|
$replaceAlt = $attr;
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
* /wire/core/Fieldtype.php
|
* /wire/core/Fieldtype.php
|
||||||
* /wire/core/FieldtypeMulti.php
|
* /wire/core/FieldtypeMulti.php
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -19,7 +19,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
public static function getModuleInfo() {
|
public static function getModuleInfo() {
|
||||||
return array(
|
return array(
|
||||||
'title' => 'Page Reference',
|
'title' => 'Page Reference',
|
||||||
'version' => 104,
|
'version' => 105,
|
||||||
'summary' => 'Field that stores one or more references to ProcessWire pages',
|
'summary' => 'Field that stores one or more references to ProcessWire pages',
|
||||||
'permanent' => true,
|
'permanent' => true,
|
||||||
);
|
);
|
||||||
@@ -1367,8 +1367,6 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
/**
|
/**
|
||||||
* Return pages referencing the given $page, optionally indexed by field name
|
* Return pages referencing the given $page, optionally indexed by field name
|
||||||
*
|
*
|
||||||
* Not currently applicable, for future use.
|
|
||||||
*
|
|
||||||
* The default behavior when no arguments are provided (except $page) is to simply return a PageArray of all pages
|
* The default behavior when no arguments are provided (except $page) is to simply return a PageArray of all pages
|
||||||
* referencing the given one (excluding hidden, unpublished, trash, no-access, etc.). Specify "include=all"
|
* referencing the given one (excluding hidden, unpublished, trash, no-access, etc.). Specify "include=all"
|
||||||
* as your $selector if you want to include all pages without filtering. If the quantity may be large,
|
* as your $selector if you want to include all pages without filtering. If the quantity may be large,
|
||||||
@@ -1377,26 +1375,37 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
*
|
*
|
||||||
* @param Page $page Page to get references for
|
* @param Page $page Page to get references for
|
||||||
* @param string|bool $selector Optionally filter/modify returned result, i.e. "limit=10, include=all", etc.
|
* @param string|bool $selector Optionally filter/modify returned result, i.e. "limit=10, include=all", etc.
|
||||||
* Or specify boolean TRUE to get a count.
|
* Or boolean TRUE as shortcut for "include=all".
|
||||||
* @param bool|Field|null $field Optionally specify Field to limit results to (default includes all fields of this type),
|
* @param bool|Field|null $field Optionally specify Field to limit results to (default includes all fields of this type),
|
||||||
* Or boolean TRUE to return array indexed by field name.
|
* Or boolean TRUE to return array indexed by field name.
|
||||||
|
* @param bool $getCount Specify true to get a count (int) rather than a PageArray (default=false)
|
||||||
* @return PageArray|array|int Returns one of the following, according to the provided arguments:
|
* @return PageArray|array|int Returns one of the following, according to the provided arguments:
|
||||||
* - returns PageArray as default behavior, including when given a $selector string and/or Field object.
|
* - returns PageArray as default behavior, including when given a $selector string and/or Field object.
|
||||||
* - returns array of PageArray objects if $field argument is TRUE ($selector may be populated string or blank string).
|
* - returns array of PageArray objects if $field argument is TRUE ($selector may be populated string or blank string).
|
||||||
* - returns int if the count option (boolean true) specified for $selector.
|
* - returns int if the count option (boolean true) specified for $selector.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private function findReferences(Page $page, $selector = '', $field = false) {
|
public function findReferences(Page $page, $selector = '', $field = false, $getCount = false) {
|
||||||
|
|
||||||
|
/** @var Pages $pages */
|
||||||
|
$pages = $this->wire('pages');
|
||||||
|
|
||||||
// modifier option defaults
|
// modifier option defaults
|
||||||
$getCount = false;
|
|
||||||
$byField = false;
|
$byField = false;
|
||||||
|
$includeAll = $selector === true || $selector === "include=all";
|
||||||
|
$findLimit = 200;
|
||||||
$fieldName = '';
|
$fieldName = '';
|
||||||
|
|
||||||
// determine which modifier options are used
|
// determine whether to use include=all
|
||||||
if($selector === true) {
|
if($selector === true) {
|
||||||
$getCount = true;
|
$selector = "include=all";
|
||||||
$selector = '';
|
} else if(strlen($selector) && !$includeAll && strpos($selector, "include=all") !== false) {
|
||||||
|
foreach(new Selectors($selector) as $s) {
|
||||||
|
if($s->field() === 'include' && $s->value() === 'all') {
|
||||||
|
$includeAll = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_bool($field) || is_null($field)) {
|
if(is_bool($field) || is_null($field)) {
|
||||||
@@ -1411,48 +1420,72 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
// results
|
// results
|
||||||
$fieldNames = array();
|
$fieldNames = array(); // field names that point to $page, array of [ field_id => field_name ]
|
||||||
$fieldCounts = array();
|
$fieldCounts = array(); // counts indexed by field name (if count mode)
|
||||||
$total = 0;
|
$total = 0;
|
||||||
|
|
||||||
|
// first determine which fields have references to $page
|
||||||
foreach($this->wire('fields') as $field) {
|
foreach($this->wire('fields') as $field) {
|
||||||
|
|
||||||
if($fieldName && $field->name != $fieldName) continue;
|
if($fieldName && $field->name != $fieldName) continue;
|
||||||
if(!$field->type instanceof FieldtypePage) continue;
|
if(!$field->type instanceof FieldtypePage) continue;
|
||||||
|
|
||||||
$table = $field->getTable();
|
$table = $field->getTable();
|
||||||
$sql = "SELECT COUNT(*) FROM `$table` WHERE data=:id";
|
$sql = "SELECT COUNT(*) FROM `$table` WHERE data=:id";
|
||||||
$query = $this->wire('database')->prepare($sql);
|
$query = $this->wire('database')->prepare($sql);
|
||||||
$query->bindValue(':id', $page->id, \PDO::PARAM_INT);
|
$query->bindValue(':id', $page->id, \PDO::PARAM_INT);
|
||||||
$query->execute();
|
$query->execute();
|
||||||
|
|
||||||
$cnt = (int) $query->fetchColumn();
|
$cnt = (int) $query->fetchColumn();
|
||||||
if($cnt) {
|
if($cnt > 0) {
|
||||||
$fieldNames[$field->id] = $field->name;
|
$fieldNames[$field->id] = $field->name;
|
||||||
$fieldCounts[$field->name] = $cnt;
|
$fieldCounts[$field->name] = $cnt;
|
||||||
$total += $cnt;
|
$total += $cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->closeCursor();
|
$query->closeCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if they just asked for the count, then we have all that we need to finish now
|
||||||
|
if($getCount && $includeAll) {
|
||||||
// return count or array of counts
|
// return count or array of counts
|
||||||
if($getCount) return $byField ? $fieldCounts : $total;
|
return $byField ? $fieldCounts : $total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there was nothing found, finish early
|
||||||
|
if(!$total) {
|
||||||
// no references found and PageArray requested, return blank PageArray
|
// no references found and PageArray requested, return blank PageArray
|
||||||
if(!$total) return $this->wire('pages')->newPageArray();
|
return $byField ? array() : $pages->newPageArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform another find() to filter results, and return requested result type
|
||||||
if($byField) {
|
if($byField) {
|
||||||
// return array of PageArrays indexed by fieldName
|
// return array of PageArrays indexed by fieldName
|
||||||
$result = array();
|
$result = array();
|
||||||
foreach($fieldNames as $fieldName) {
|
foreach($fieldNames as $fieldName) {
|
||||||
$selector = "$fieldName=$page->id, $selector";
|
$s = rtrim("$fieldName=$page->id, $selector", ', ');
|
||||||
$items = $this->wire('pages')->find(rtrim($selector, ", "));
|
if($getCount) {
|
||||||
if($items->count()) {
|
$cnt = $pages->count($s);
|
||||||
$result[$fieldName] = $items;
|
if($cnt) $result[$fieldName] = $cnt;
|
||||||
|
} else {
|
||||||
|
if($total > $findLimit) {
|
||||||
|
$items = $pages->findMany($s);
|
||||||
|
} else {
|
||||||
|
$items = $pages->find($s);
|
||||||
|
}
|
||||||
|
if($items->count()) $result[$fieldName] = $items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// return PageArray of all references
|
// return PageArray of all references
|
||||||
$selector = implode('|', $fieldNames) . "=$page->id, $selector";
|
$selector = rtrim(implode('|', $fieldNames) . "=$page->id, $selector", ', ');
|
||||||
$result = $this->wire('pages')->find(rtrim($selector, ", "));
|
if($getCount) {
|
||||||
|
$result = $pages->count($selector);
|
||||||
|
} else if($total > $findLimit) {
|
||||||
|
$result = $pages->findMany($selector);
|
||||||
|
} else {
|
||||||
|
$result = $pages->find($selector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
* For documentation about the fields used in this class, please see:
|
* For documentation about the fields used in this class, please see:
|
||||||
* /wire/core/Fieldtype.php
|
* /wire/core/Fieldtype.php
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* Properties set to $field that is using this type, acceessed by $field->get('property'):
|
* Properties set to $field that is using this type, acceessed by $field->get('property'):
|
||||||
@@ -24,7 +24,7 @@ class FieldtypeTextarea extends FieldtypeText {
|
|||||||
public static function getModuleInfo() {
|
public static function getModuleInfo() {
|
||||||
return array(
|
return array(
|
||||||
'title' => 'Textarea',
|
'title' => 'Textarea',
|
||||||
'version' => 106,
|
'version' => 107,
|
||||||
'summary' => 'Field that stores multiple lines of text',
|
'summary' => 'Field that stores multiple lines of text',
|
||||||
'permanent' => true,
|
'permanent' => true,
|
||||||
);
|
);
|
||||||
@@ -373,5 +373,43 @@ class FieldtypeTextarea extends FieldtypeText {
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find abstracted HTML/href attribute Textarea links to given $page
|
||||||
|
*
|
||||||
|
* @param Page $page Find links to this page
|
||||||
|
* @param string|bool $selector Optionally filter by selector or specify boolean true to assume "include=all".
|
||||||
|
* @param string|Field $field Optionally limit to searching given field name/instance.
|
||||||
|
* @param array $options Options to modify return value:
|
||||||
|
* - `getIDs` (bool): Return array of page IDs rather than Page instances. (default=false)
|
||||||
|
* - `getCount` (bool): Return a total count (int) of found pages rather than Page instances. (default=false)
|
||||||
|
* - `confirm` (bool): Confirm that the links are present by looking at the actual page field data. (default=true)
|
||||||
|
* You can specify false for this option to make it perform faster, but with a potentially less accurate result.
|
||||||
|
* @return PageArray|array|int
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function findLinks(Page $page, $selector = '', $field = '', array $options = array()) {
|
||||||
|
|
||||||
|
$searchFields = array();
|
||||||
|
if($selector === true) $selector = "include=all";
|
||||||
|
|
||||||
|
foreach($this->wire('fields') as $f) {
|
||||||
|
if($field) {
|
||||||
|
if("$f" != "$field") continue;
|
||||||
|
} else {
|
||||||
|
// limit to fields with contentTypeHTML and htmlLinkAbstract
|
||||||
|
$contentType = $f->get('contentType');
|
||||||
|
if(empty($contentType)) continue;
|
||||||
|
if($contentType != self::contentTypeHTML && $contentType != self::contentTypeImageHTML) continue;
|
||||||
|
$htmlOptions = $f->get('htmlOptions');
|
||||||
|
if(!is_array($htmlOptions) || !in_array(self::htmlLinkAbstract, $htmlOptions)) continue;
|
||||||
|
}
|
||||||
|
$searchFields[$f->name] = $f->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!count($searchFields)) return $this->wire('pages')->newPageArray();
|
||||||
|
|
||||||
|
return $this->markupQA()->findLinks($page, $searchFields, $selector, $options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user