WireSaveableItems
Abstract base class that manages collections of saveable items backed by a database table
It provides
the CRUD (Create, Read, Update, Delete) and find operations shared by ProcessWire's
$fields and $templates API variables. You won't
instantiate this class directly — it powers the collections you already use.
// $fields and $templates are both WireSaveableItems instances
$field = $fields->get('body');
$template = $templates->get('basic-page');
// Find by selector string
$textFields = $fields->find('type=text');
// Iterate (implements IteratorAggregate)
foreach($fields as $field) {
echo "$field->name: $field->label\n";
} Wire extends Wire and implements \IteratorAggregate. Items in the collection
must implement the Saveable interface (providing save(), getTableData(), and id/name properties).
Expand all Collapse all API reference
You don't create Wire directly. Access the concrete subclasses:
$fields = $fields; // API variable — Fields instance
$templates = $templates; // API variable — Templates instance
$fields = $wire->fields;
$templates = $wire->templates;get($key)
Retrieve an item by its id (int) or name (string). With lazy loading enabled, triggers
a single-item load from the database if the item hasn't been preloaded. Returns null if not found.
$field = $fields->get('body'); // by name
$field = $fields->get(123); // by id
$template = $templates->get('home'); // by nameThe class also supports __invoke, so on API variables you can use shorthand:
$field = $fields('body');
$template = $templates('home');has($item)
Check whether an item exists in the collection. Accepts the same arguments as get() (id, name, or item object).
if($fields->has('body')) {
// field 'body' exists
}
if($fields->has($someField)) {
// the given Field object is in the collection
}find($selectors)
Find items matching a selector string or Selectors object. This delegates to the internal
WireArray's find(). When lazy loading is active, all items are loaded first.
$textFields = $fields->find('type=FieldtypeText');
$systemTemplates = $templates->find('flags&' . Template::flagSystem);save(Saveable $item)
Save (insert or update) the given item to the database. If the item has an id, it's updated;
otherwise it's inserted and assigned a new id. Returns true on success, false on failure.
$field = $fields->get('body');
$field->label = 'New Label';
$fields->save($field);Triggers saveReady before the write and saved after. For new items, also triggers added.
delete(Saveable $item)
Delete the item from the database and remove it from the collection. Returns true on success,
false on failure. The item's id is set to 0 after deletion.
$template = $templates->get('obsolete');
$templates->delete($template);Triggers deleteReady before the delete and deleted after.
clone(Saveable $item, $name = '')
Create and save a clone of the given item. If the item uses a name field, it automatically
appends a numeric suffix to ensure uniqueness (e.g. body_1, body_2). You may optionally
specify a new name.
$original = $fields->get('body');
$copy = $fields->clone($original, 'body_clone');
if($copy) {
echo "Cloned to: $copy->name";
}Triggers cloneReady before the clone and cloned after.
getRaw($key)
Get the raw database row for an item by ID or name, bypassing any cache and without creating
a fully-initialized object. Returns an associative array of column values, or null if not found.
$row = $fields->getRaw('body');
// $row = ['id' => 123, 'name' => 'body', 'label' => 'Body', ...]getFresh($key)
Load and return a fresh instance of an item from the database, bypassing any cache. The returned
item is not added to the collection. Returns null if not found.
$freshField = $fields->getFresh('body');
// $freshField is a standalone Field object, not in $fields collectiongetWireArray()
Return the internal WireArray container without triggering lazy loads. This is the guaranteed
no-side-effect version of getAll(). Marked #pw-internal — prefer get() or find() for
normal use.
$items = $fields->getWireArray();
// Raw WireArray — won't trigger lazy loadsWire implements \IteratorAggregate, so you can iterate directly with foreach.
If lazy loading is active, iterating triggers loading of all remaining items.
foreach($fields as $field) {
echo "$field->name: $field->type\n";
}Wire provides a rich set of hookable lifecycle methods. These are the key
extension points for reacting to changes in fields and templates.
Lifecycle hooks table
| Hook | When | Arguments |
|---|---|---|
Fields::saveReady | Confirmed item will be saved | $item |
Fields::saved | After item has been saved | $item, $changes |
Fields::added | After a new item has been added | $item |
Fields::deleteReady | Confirmed item will be deleted | $item |
Fields::deleted | After item has been deleted | $item |
Fields::cloneReady | Confirmed item will be cloned | $item, $copy |
Fields::cloned | After item has been cloned | $item, $copy |
Fields::renameReady | Item is about to be renamed | $item, $oldName, $newName |
Fields::renamed | After item has been renamed | $item, $oldName, $newName |
Replace Fields with Templates to hook the templates collection. Hooks on Wire
itself won't fire — hook the concrete class.
Hook examples
// Log every field save
$wire->addHookAfter('Fields::saved', function(HookEvent $event) {
$fields = $event->object; /** @var Fields $fields */
$field = $event->arguments(0); /** @var Field $field */
$changes = $event->arguments(1);
$wire = $event->wire();
$wire->log->save('fields', "Field '$field->name' saved. Changes: " . implode(', ', $changes));
});
// Enforce a naming convention on new templates
$wire->addHookBefore('Templates::saveReady', function(HookEvent $event) {
$item = $event->arguments(0);
if(!$item->id && !str_starts_with($item->name, 'tpl_')) {
$item->name = 'tpl_' . $item->name;
}
});When enabled via $config->useLazyLoading, Wire loads item data from the
database on demand rather than all at once. By default in ProcessWire 3.x, lazy loading is
enabled for both $fields and $templates.
- Items are loaded individually when accessed via
get()by name or ID - Iteration (
foreach) triggers a full load of all remaining items find()triggers a full load before filtering- Use
getWireArray()to inspect the collection without triggering loads getRaw()andgetFresh()bypass the cache entirely and always query the database
For collections backed by a lookup table (many-to-many relationship), ProcessWire provides
the Wire subclass. It overrides save() and delete() to also
manage records in the lookup table. Items must implement the HasLookupItems interface.
This is used internally by classes like Fieldgroups (template-fieldgroup relationships).
- API variables:
$fieldsand$templatesare the two concrete instances of this class. Their docs at Fields and Templates cover the class-specific additions on top of this shared base. - Saveable interface: All items managed by this class must implement
Saveable, providinggetTableData()(returns data matching DB columns),save(), andid/nameproperties. - No fuel scoping:
useFuel()returnsfalse, so within subclasses ofWire, API variables are not accessible asSaveable Items $this->apivar. Use$this->wire('apivar')or$this->wire()->apivarinstead. - Database table: Each subclass defines its own table via
getTable(). Columns must match the keys returned by the item'sgetTableData(). - Source file:
wire/core/WireSaveable Items/Wire Saveable Items.php - Lookup subclass:
wire/core/WireSaveable Items/Wire Saveable ItemsLookup.php - Related interfaces:
wire/core/WireSaveable Items/Interfaces.php
API reference: methods, hooks
Click any linked item for full usage details and examples. Hookable methods are indicated with the icon. In addition to those shown below, the Wire class also inherits all the methods and properties of: Wire
Common
Additional methods and properties
In addition to the methods and properties above, Wire
API reference based on ProcessWire core version 3.0.269