praiadeseselle/wire/core/WireSaveableItemsLookup.php

249 lines
7.1 KiB
PHP
Raw Normal View History

2022-03-08 15:55:41 +01:00
<?php namespace ProcessWire;
/**
* ProcessWire WireSaveableItemsLookup
*
* Provides same functionality as WireSaveableItems except that this class includes joining/modification of a related lookup table
*
2022-11-05 18:32:48 +01:00
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
2022-03-08 15:55:41 +01:00
* https://processwire.com
*
*/
abstract class WireSaveableItemsLookup extends WireSaveableItems {
/**
* If a lookup table should be left joined, this method should return the table name
*
*/
abstract public function getLookupTable();
2022-11-05 18:32:48 +01:00
/**
* Cache of value returned from getLookupField() method
*
* @var string|null
*
*/
protected $lookupField = null;
2022-03-08 15:55:41 +01:00
/**
* If a lookup table should be left joined, this method returns the name of the array field in $data that contains multiple values
*
* i.e. roles_permissions becomes permissions_id if getTable() returns roles
* Does not need to be overridden unless the table naming structure doesn't follow existing logic.
*
*/
public function getLookupField() {
2022-11-05 18:32:48 +01:00
if($this->lookupField) return $this->lookupField;
2022-03-08 15:55:41 +01:00
$lookupTable = $this->getLookupTable();
if(!$lookupTable) return '';
2022-11-05 18:32:48 +01:00
$this->lookupField = preg_replace('/_?' . $this->getTable() . '_?/', '', $lookupTable) . '_id';
return $this->lookupField;
2022-03-08 15:55:41 +01:00
}
/**
* Get the DatabaseQuerySelect to perform the load operation of 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) {
$query = parent::getLoadQuery($selectors);
2022-11-05 18:32:48 +01:00
$database = $this->wire()->database;
2022-03-08 15:55:41 +01:00
$table = $database->escapeTable($this->getTable());
$lookupTable = $database->escapeTable($this->getLookupTable());
$lookupField = $database->escapeCol($this->getLookupField());
$query->select("$lookupTable.$lookupField"); // QA
2022-11-05 18:32:48 +01:00
$query->leftjoin("$lookupTable ON $lookupTable.{$table}_id=$table.id ")->orderby("sort");
// $query->leftjoin("$lookupTable ON $lookupTable.{$table}_id=$table.id ")->orderby("$table.id, $lookupTable.sort");
2022-03-08 15:55:41 +01:00
return $query;
}
/**
* Load items from the database table and return them in the same type class that getAll() returns
*
* A selector string or Selectors may be provided so that this can be used as a find() by descending classes that don't load all items at once.
*
* @param WireArray $items
* @param Selectors|string|null $selectors Selectors or a selector string to find, or NULL to load all.
* @return WireArray Returns the same type as specified in the getAll() method.
*
*/
protected function ___load(WireArray $items, $selectors = null) {
2022-11-05 18:32:48 +01:00
$useLazy = $this->useLazy();
$database = $this->wire()->database;
2022-03-08 15:55:41 +01:00
$query = $this->getLoadQuery($selectors);
$sql = $query->getQuery();
2022-11-05 18:32:48 +01:00
$this->getLookupField(); // preload
2022-03-08 15:55:41 +01:00
$stmt = $database->prepare($sql);
$stmt->execute();
2022-11-05 18:32:48 +01:00
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// note: non-use of lazyNameIndex/lazyIdIndex is intentional
foreach($rows as $row) {
if($useLazy) {
$this->lazyItems[] = $row;
2022-03-08 15:55:41 +01:00
} else {
2022-11-05 18:32:48 +01:00
/** @var HasLookupItems $item */
$this->initItem($row, $items);
2022-03-08 15:55:41 +01:00
}
}
2022-11-05 18:32:48 +01:00
2022-03-08 15:55:41 +01:00
$stmt->closeCursor();
$items->setTrackChanges(true);
2022-11-05 18:32:48 +01:00
2022-03-08 15:55:41 +01:00
return $items;
}
2022-11-05 18:32:48 +01:00
/**
* Create a new Saveable/Lookup item from a raw array ($row) and add it to $items
*
* @param array $row
* @param WireArray|null $items
* @return Saveable|HasLookupItems|WireData|Wire
* @since 3.0.194
*
*/
protected function initItem(array &$row, WireArray $items = null) {
$lookupField = $this->getLookupField();
$lookupValue = $row[$lookupField];
$item = $this->makeBlankItem(); /** @var HasLookupItems $item */
if($items === null) $items = $this->getWireArray();
unset($row[$lookupField]);
$item->addLookupItem($lookupValue, $row);
foreach($row as $key => $value) {
$item->$key = $value;
}
if($this->useLazy) {
$items->add($item);
foreach($this->lazyItems as $key => $a) {
if($a['id'] != $row['id']) continue;
if(!isset($a[$lookupField])) continue;
$lookupValue = $a[$lookupField];
unset($a[$lookupField]);
$item->addLookupItem($lookupValue, $a);
unset($this->lazyItems[$key]);
}
} else if($items->has($item)) {
// LEFT JOIN is adding more elements of the same item, i.e. from lookup table
// if the item is already present in $items, then use the existing one rather
// and throw out the one we just created
$item = $items->get($item);
$item->addLookupItem($lookupValue, $row);
} else {
// add a new item
$items->add($item);
}
return $item;
}
2022-03-08 15:55:41 +01:00
/**
* Should the given item key/field be saved in the database?
*
* Template method used by ___save()
*
* @param string $key
* @return bool
*
*/
protected function saveItemKey($key) {
if($key == $this->getLookupField()) return false;
return parent::saveItemKey($key);
}
/**
* Save the provided item to database
*
* @param Saveable $item
* @return bool
* @throws WireException
*
*/
public function ___save(Saveable $item) {
if(!$item instanceof HasLookupItems) {
$class = $this->className();
throw new WireException("$class::save() requires an item that implements HasLookupItems interface");
}
2022-11-05 18:32:48 +01:00
$database = $this->wire()->database;
2022-03-08 15:55:41 +01:00
$lookupTable = $database->escapeTable($this->getLookupTable());
$lookupField = $database->escapeCol($this->getLookupField());
$table = $database->escapeTable($this->getTable());
$item_id = (int) $item->id;
if($item_id) {
$query = $database->prepare("DELETE FROM $lookupTable WHERE {$table}_id=:item_id");
$query->bindValue(":item_id", $item_id, \PDO::PARAM_INT);
$query->execute();
}
$result = parent::___save($item);
$item_id = (int) $item->id; // reload, in case it was 0 before
$sort = 0;
2022-11-05 18:32:48 +01:00
if($item_id) {
$sql = "INSERT INTO $lookupTable SET {$table}_id=:item_id, $lookupField=:value_id, sort=:sort";
$query = $database->prepare($sql);
2023-03-10 19:41:40 +01:00
foreach($item->getLookupItems() as $value) {
2022-11-05 18:32:48 +01:00
$value_id = (int) $value->id;
$query->bindValue(":item_id", $item_id, \PDO::PARAM_INT);
$query->bindValue(":value_id", $value_id, \PDO::PARAM_INT);
$query->bindValue(":sort", $sort, \PDO::PARAM_INT);
$query->execute();
$sort++;
}
2022-03-08 15:55:41 +01:00
$this->resetTrackChanges();
}
return $result;
}
/**
* Delete the provided item from the database
*
* @param Saveable $item
* @return bool
*
*/
public function ___delete(Saveable $item) {
2022-11-05 18:32:48 +01:00
$database = $this->wire()->database;
2022-03-08 15:55:41 +01:00
$lookupTable = $database->escapeTable($this->getLookupTable());
$table = $database->escapeTable($this->getTable());
$item_id = (int) $item->id;
$query = $database->prepare("DELETE FROM $lookupTable WHERE {$table}_id=:item_id"); // QA
$query->bindValue(":item_id", $item_id, \PDO::PARAM_INT);
$query->execute();
return parent::___delete($item);
}
2022-11-05 18:32:48 +01:00
/**
* debugInfo PHP 5.6+ magic method
*
* This is used when you print_r() an object instance.
*
* @return array
*
*/
public function __debugInfo() {
$info = parent::__debugInfo();
$info['loaded'] = array_unique($info['loaded']);
$info['notLoaded'] = array_unique($info['notLoaded']);
return $info;
}
2022-03-08 15:55:41 +01:00
}