From a8fed8efec6a9b5c5d16d1c03c79eaf94a165c51 Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Tue, 25 Nov 2025 22:28:46 +0000 Subject: [PATCH 1/2] Table documentation --- .../table/_assets/nested-set-model.png | Bin 0 -> 59171 bytes docs/general-concepts/table/advanced-table.md | 364 ++++++++++++++++++ docs/general-concepts/table/basic-table.md | 198 ++++++++++ docs/general-concepts/table/index.md | 17 + docs/general-concepts/table/nested.md | 290 ++++++++++++++ 5 files changed, 869 insertions(+) create mode 100644 docs/general-concepts/table/_assets/nested-set-model.png create mode 100644 docs/general-concepts/table/advanced-table.md create mode 100644 docs/general-concepts/table/basic-table.md create mode 100644 docs/general-concepts/table/index.md create mode 100644 docs/general-concepts/table/nested.md diff --git a/docs/general-concepts/table/_assets/nested-set-model.png b/docs/general-concepts/table/_assets/nested-set-model.png new file mode 100644 index 0000000000000000000000000000000000000000..ddccf1c7c0d4e92bae80f89f32c08fff32e5d4bc GIT binary patch literal 59171 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 array(10 => 1, 11 => 0), "core.delete" => array(10 => 0, 11 => 1)) +``` + +where 10 and 11 are ids of usergroups, and 1 is Allowed and 0 is Denied. In this example: + +- core.edit entry: usergroup 10 is allowed to edit the item, while usergroup 11 is denied edit access +- core.delete entry: usergroup 11 can delete the item, while usergroup 10 is denied delete access + +The `$input` parameter can be rules in the above array format, or in the equivalent json-encoded format, +or in the Joomla Rules object format which is generated from passing either form to the Rules class constructor. + +If you're developing a component which has ACL permissions on individual items, +then use the [standard form field "rules"](../forms-fields/standard-fields/rules.md) in your XML edit file. + +```xml + +``` + +Then in `bind()` method you can call (where `$data` holds the array of values sent in the HTTP POST request): + +```php +$this->setRules($data['rules']); +``` + +If you call `setRules()` to set the ACL permissions which should be applied to the record, +and then call `store()` to save the record to the database, +then the Table class functionality will update also the associated record in the assets table; you don't have to do this yourself. + +### Get Asset helper functions + +In writing new assets records the Table class needs to know an additional 3 items of information, +which you can see by browsing the assets records using phpmyadmin, for example. +These 3 items of information it requests by calling 3 protected functions, +which Joomla expects that you will provide in your own table class which inherits from Joomla Table. These are + +- `_getAssetName()` – to provide the name field – this is usually of the form "com_example.item.12" +- `_getAssetTitle()` - to provide the title field – this is usually the title of the com_example item +- `_getAssetParentId()` – to provide the position of the asset record in the asset hierarchy +(implemented as a tree structure in the assets table using the Nested Model), by returning the parent asset id. +For a com_example item this would usually be the record with just "com_example" as the name, +ie the general ACL permissions for the com_example component. + +Examples of these are in the sample module code at the end of this guide. + +## Ordering + +Many Joomla items include the concept of ordering, for example, modules have an order in which they are displayed in each possible template position. +This is governed by a field called `ordering` in the database table, +and when items are selected from the database the SQL query includes an ORDER BY `ordering` clause if they're to be output in order. + +The Joomla Table class has 3 methods which relate to ordering. + +### getNextOrder + +```php +getNextOrder(string $where) +``` + +This determines the max value of the ordering field among the records selected by the optional where clause `$where`, and returns this max + 1. + +Don't include the word "WHERE" in the `$where` string. + +This function is useful if you're inserting a record into your table +and you want it to appear at the end of the group of records identified in the where clause - +you can set your ordering value to what is returned by `getNextOrder()`. +If you want it to appear at the start you can set the ordering field to 0 (zero) and then use `reorder()` below. + +### Reorder + +```php +reorder(string $where) +``` + +This function reads the set of N records defined by the where clause `$where`, using ORDER BY `ordering` +and then writes them back with the ordering fields nicely numbered from 1 to N. + +### Move + +```php +move(integer $delta, string $where) +``` + +This function basically finds the record with the next greater (if `$delta` is positive) or lesser (if `$delta` is negative) +value of the ordering field, and it then swaps the ordering values of the two records. + +Despite what the Joomla API definition says, the magnitude of `$delta` is irrelevant – +the function always just moves the record by 1 in the ordering sequence. + +This function might be useful if you provided users with a user interface which allowed them to move records up or down by one. +However, Joomla provides a more sophisticated mechanism built using javascript for dragging a record to a new position. +When a record is dragged the javascript recalculates the ordering values of all the displayed records +and sends the updated ordering values to the server in an ajax request, and they are then written to the database. +So if you used that approach, then you wouldn't need this `move` function. + +## Publish + +```php +publish(mixed $pks = null, integer $state = 1, integer $userId) +``` + +This sets the `publish` field to `$state` for the set of records identified by the `$pks` primary key(s) array. +These publish states in Joomla are conventionally identified by integer values, eg: + +- 0 - unpublished +- 1 - published +- 2 - archived +- -2 - trashed + +It also sets the `publish_up` field (if it exists) to the current date/time. +However the condition in the source code for doing this is if the *currently loaded record* has a blank `publish_up` value +(rather than if one of the records identified by `$pks` has a blank `publish_up` value). (This looks like a bug). + +If `$pks` is an `array(field1 => value1, field2 => value2, …)` +then this gets mapped to the SQL where clause WHERE field1 = value1 AND field2 = value2, … +to identify the records to be updated. If `$pks` is absent then just the current loaded record is affected. + +Any records in this set which are checked out to a user other than `$userId` are excluded. +If there are no such records then any records of the affected set which are checked out to the user identified by `$userId` are checked in. + +Note that this API isn't widely used within the core Joomla application, and you may wish to follow the example of main Joomla +components and use the `publish()` method with the MVC AdminModel class, rather than use this API. + +## Hits + +```php +hit(mixed $pk = null) +``` + +A hits counter is usually used to count the number of times a webpage is visited. +In Joomla you can record how many times a component is displayed on the frontend by using adding a hits column to the component's database table, +and calling this function each time the component is displayed. + +The function simply increments the value in the hits field in the record identified by the `$pk` key, +or in the currently loaded record if `$pk` is null. + +## Reflection Methods + +There are several reflection-like methods which provide information about the table which is being accessed + +**`getTableName`** – returns the name of the table, "#__modules" for example + +**`getKeyName`** – returns the name(s) of the primary key field(s) + +**`getPrimaryKeys`** – returns an array of the primary key(s) and value(s) + +**`getFields`** – returns an array of the names of the columns of the database table + +**`hasPrimaryKey`** – checks if the primary key has a value set + +**`hasField($name)`** – checks if the table has a field of that `$name` + +## Reserved Column Names and Aliases + +Much of the functionality associated with the Table class relies upon certain columns being given specific names, +such as "ordering", "checked_out" and "asset_id". +The presence of a column named with one of these special names is sufficient to switch on functionality in the Table class. + +For a number of these names it is possible to set up an alias, +so that if the field in which you store your published state is called "state" +then you can use setColumnAlias() to set up an alias, for example, + +```php +$table->setColumnAlias('published', 'state'); +``` + +The corresponding getter function is, + +```php +$alias = $table->getColumnAlias('published'); +``` + +which using the example above would return the string "state". + +In this way you are free to follow your own column naming convention but at the same time get access to Joomla core functionality. +However, it's my opinion that you shouldn't take that approach, +and instead you should name your columns to follow exactly the Joomla approach. +The reason is that while Joomla helps you out with aliases here, +there are other areas of code where aliases for those fields may not be supported, either now or in the future. + +In particular Joomla core javascript code assumes that certain fields are given certain names, and doesn't always support aliases. +An exception to the above is using the name "state" for the published state of a record instead of "published". +Because com_content and other core Joomla components use "state" you are pretty safe taking that approach, +because new code will always have to support those core components. + +For reference, the fields with special meanings are listed in the table below + +| Field names | Meaning | +|-------------|---------| +| **The following fields can be aliased in the Table class** || +| checked_out, checked_out_time | the userid to which the record is checked out, and the checkout date/time | +| hits | the number of hits for that item | +| ordering | the integer reflecting the record's order | +| published | the published state of the item (Unpublished / Published / Trashed etc). Some core Joomla components use "state" for this column | +| publish_up | the date/time the record was first published | +| **The following fields have special meanings within Table, but cannot be aliased** || +| asset_id | the foreign key pointing to the associated assets record | +| **The following fields have special meanings elsewhere in Joomla (this isn't an exhaustive list)** || +| id | the autogenerated id of the table | +| title | the title of the item | +| alias | the alias of the item | +| language | the language code | +| access | the id of the Access Level | +| params | a JSON-encoded string of parameters | +| created_user_id, created_time | userid of user who created the item, and date/time | +| modified_user_id, modified_time | userid of user who modified the item, and date/time | +| catid | id of the assigned category | +| parent_id, lft, rgt, level, path | fields associated with nested tables | + +## Sample Module + +The example module below incorporates several aspects described above. +It's the same module as in the previous section describing basic table operations. + +### Module Code + +Download the module code from the Manual Examples (tbd). + +### Module Installation + +Zip up the mod_table_example directory to create mod_table_example.zip. + +Within your Joomla administrator go to Install Extensions and via the Upload Package File tab +select this zip file to install this sample mod_table_example module. + +Make this module visible by editing it (click on it within the Modules page) then: + +- making its status Published +- selecting a position on the page for it to be shown +- on the menu assignment tab specify the pages it should appear on + +### Using the module + +When you navigate to your site page where the module is displayed, then the module code will run. + +The module contains functionality relating to this page (in the function `doAdvancedTableOperations`), +as well as the function `doBasicTableOperations` which relates to the previous section. + +It uses its own record in modules table to illustrate Table method calls, specifically: + +- using `isCheckedOut()` to check if a record is checked out or not +- calling `getRules()` to obtain the ACL, but as explained above, this returns null +- defining the ACL rules for the module record using `setRules()`, including defining the 3 protected functions to provide values for fields in the assets table +- use of the reflection method `getTableName()` +- use of the some of the ordering methods diff --git a/docs/general-concepts/table/basic-table.md b/docs/general-concepts/table/basic-table.md new file mode 100644 index 00000000..53bb409a --- /dev/null +++ b/docs/general-concepts/table/basic-table.md @@ -0,0 +1,198 @@ +--- +title: Basic Table Functionality +sidebar_position: 1 +--- + +This page covers basic functionality of the Joomla Table class. + +The API definitions are at [Joomla Table APIs](cms-api://classes/Joomla-CMS-Table-Table.html). + +Introduction +============ + +The Joomla Table class (Joomla\CMS\Table\Table in libraries/src/Table/Table.php) +provides a framework which enables you to do CRUD operations (and more) on database tables. + +The usual case is that you are developing a component with its own database table, +and you use the Table class to implement administrator CRUD operations for data in your database table. + +The Joomla Table class is an abstract class, so you must create your own table class (for your component's database table) which extends the Joomla Table class. + +## Instantiating your table class + +If you're developing a component with its own database table or tables, +then you should use the [Joomla MVC pattern](../../building-extensions/components/mvc/index.md), +and use the MVC Factory for creating controller, view, model and table instances. + +Then within your model you can use + +```php +$table = $this->getTable(); // if your Table class matches the view= parameter in the HTTP request +$table = $this->getTable('example'); // to get the ExampleTable class +``` + +:::note[TODO] + Add an appropriate link to the updated component development tutorial when it's written. +::: + +If you're developing a module or plugin then you don't have access to the MVC Factory, +and the easiest way is to instantiate it directly, passing the database object: + +```php +$table = new ExampleTable($dbo); +``` + +See the sample module code at the end of this page for an example. + +Your table class defines 2 important items of data: + +1. The name of your database table, in the format "#__example". Joomla adds the prefix specified for the database + +2. The primary key (one or more fields) of the database table + +## Basic Table functions + +There are several basic functions within the Table class. +These are described below for a database table with 2 fields: + +- `id` - the primary key +- `title` - a descriptive text field + +### load() + +The `load()` method reads a record from the database and makes the fields of the record available as properties of the table object. + +```php +$id = 1; // set the id field to some value (usually based on an HTTP GET parameter) +$result = $table->load($id); // loads the record with id=1 +if ($result) // $result is true/false depending on whether the record was loaded successfully or not +{ + echo $table->title; // outputs the title associated with the currently loaded record +} +``` + +### bind() + +Use `bind($data)` when you have `$data` which you want to set into the record. +The `$data` should be in the form of an associative array. + +```php +$id = 1; +$table->load($id); +$data = array("title" => "first"); +$table->bind($data); +echo $table->title; // outputs "first" +``` + +The `bind` call has the effect of replacing the properties of the table object: + +- prior to `bind` call the `$table->title` property would have whatever was read from the database. +- after the `bind` call the `$table->title` property has whatever is passed in the `$data` array for element "title". + +### check() + +Use `check()` to perform any validation on your record before you save it to the database. +The Joomla Table class doesn't provide any default validation - it's up to you to specify what you want. + +### store() + +Use `store()` to write the database record from the table class instance. +Joomla maps the properties of the instance object to fields of the database record, and then stores the record. + +The Joomla default `store()` method examines the properties of the object relating to the primary key of the database table + +- if the primary key is not set (or id = 0 in the case of a numeric id primary key) then it issues SQL INSERT + +- if the primary key is set then it issues SQL UPDATE + +### save() + +The `save()` method is really just a concatenation of `bind()`, `check()` and `store()`. + +### delete() + +You can use the `delete()` method with or without a parameter: + +- `delete()` - deletes the record which was previously read using a `load()` call + +- `delete($key)` - deletes the record whose primary key matches `$key`. + +## CRUD Operations + +To perform CRUD operations you string together calls to the above methods + +- READ + + - `load($key)` - passing the value of the primary key + +- UPDATE + + - `load($key)` + - `bind($data)` - bind the new data + - `check()` - perform validation + - `store()` - issue the SQL UPDATE + +- CREATE + + - `bind($data)` - bind the data (a previous `load` isn't appropriate here) + - `check()` - perform validation + - `store()` - issue the SQL INSERT + +- DELETE + + - `load($key)` + - `delete()` + - or instead you can just do `delete($key)` + +## Sample Module + +This section contains the code for a simple Joomla module which you can install and run to demonstrate use of the basic Table functionality. +If you are unsure about development and installing a Joomla module +then [Module Development Tutorial](../../building-extensions/modules/module-development-tutorial/index.md) should help. + +### Module Code + +Download the module code from the Manual Examples (tbd). + +### Module Installation + +Zip up the mod_table_example directory to create mod_table_example.zip. + +Within your Joomla administrator go to Install Extensions and via the Upload Package File tab +select this zip file to install this sample mod_table_example module. + +Make this module visible by editing it (click on it within the Modules page) then: + +- making its status Published +- selecting a position on the page for it to be shown +- on the menu assignment tab specify the pages it should appear on + +### Using the module + +When you navigate to your site page where the module is displayed, then the module code will run. + +The module contains functionality relating to this page (in the function `doBasicTableOperations`), +as well as the function `doAdvancedTableOperations` which relates to the next section. + +The basic functionality demonstrates reading a record from the modules table then writing an update back to that record in the database. + +The modules table contains the module instances which are displayed on the Joomla site or administrator, +and you'll find it useful to view the table through phpmyadmin, for example. + +Some notes on the functionality of this module: + +- The module record is loaded from the database record and the title of the module is output +- The module params is json decoded into an associative array and the header tag is then set to 'h2' +(which controls how the header title is displayed in the module on the web page) +- The new note to be written to the module record is read from the `demonote` parameter in the URL, +so you can set this yourself by adding `?demonote=something` in the URL +- The new note and params are written back to the database record using `bind()` and `store()` commands. +- The params will be converted back to a json string because of the line `protected $_jsonEncode = array('params');` in our table class +- The `check()` function in our table class appends a string " added via module" to the note. + +You can verify the changes to the database record by viewing the module record within the admin back-end or by using phpmyadmin. + +:::warning + This sample module is purely for demonstrating some of the functionality of the Table class. + In general you should not alter Joomla core tables in this way, as you could corrupt the integrity of the Joomla database. +::: \ No newline at end of file diff --git a/docs/general-concepts/table/index.md b/docs/general-concepts/table/index.md new file mode 100644 index 00000000..73985291 --- /dev/null +++ b/docs/general-concepts/table/index.md @@ -0,0 +1,17 @@ +--- +title: Table +--- + +The Joomla Table class provides a framework which enables you to do CRUD operations (and more) on database tables. +It is an implementation of the [Active Record](https://en.wikipedia.org/wiki/Active_record_pattern) design pattern. +It's mainly used for the case where you're developing a Joomla component with its associated database table, +and you're providing admin functionality for managing the data in the database table +or displaying site pages associated with individual database records. + +The Table functionality can be used for batch operations if the batch operation is split into individual record updates. +However it doesn't support SQL operations on multiple records (eg selecting several records from a database table); +for this see the [Joomla Database documentation](../database/index.md). + +The Basic and Advanced table functionality relate to the standard Joomla Table class. + +The Nested Set Tables descriptions relate to tables which have a tree structure. \ No newline at end of file diff --git a/docs/general-concepts/table/nested.md b/docs/general-concepts/table/nested.md new file mode 100644 index 00000000..424e42be --- /dev/null +++ b/docs/general-concepts/table/nested.md @@ -0,0 +1,290 @@ +--- +title: Nested Set Tables +sidebar_position: 3 +--- + +Introduction +============ + +## Joomla Tree Structures + +Joomla implements menuitems and categories as tree structures. +This means, for example, that menuitems on a menu can have submenus off them (as can be seen on the Joomla admin menu) +and those submenuitems can in turn have submenus off them, and so on. +The submenus are ordered, so you can define which order the submenuitems appear. + +Joomla uses the following terminology: + +- each of the records is a **node** within the tree structure +- there is a **root node** which occupies the highest position in the hierarchy; this root node is defined as being at **level 0** +- the root node has a number of **children**, and these will be at **level 1**. Each of these children's **parent** is the root node +- these children may in turn have children of their own, at level 2, and so on +- if a node has no children, then it is known as a **leaf node** + +## Nested Sets + +To implement this tree structure within a relational database Joomla uses the [Nested Set Model](https://en.wikipedia.org/wiki/Nested_set_model). + +![Nested Set Model](./_assets/nested-set-model.png "Nested Set Model")] + +| Node | Left | Right | +|------|------|-------| +|Clothing|1|22| +|Men's|2|9| +|Suits|3|8| +|Slacks|4|5| +|Jackets|6|7| +|Women's|10|21| +|Dresses|11|16| +|Evening Gowns|12|13| +|Sun Dresses|14|15| +|Skirts|17|18| +|Blouses|19|20| + +This involves assigning to each record a left (in Joomla lft) and a right (in Joomla rgt) field, +as shown in the diagram and associated table. +You should be able to see how using the lft and rgt values you can determine for any node its level in the tree, its parent, +and for nodes with the same parent, the ordering of those nodes. + +While having lft and rgt values may be sufficient to define a tree completely in theory, +in practice adding additional fields gives more opportunity for navigating the tree efficiently, +and in Joomla there are 5 fields associated with a tree structure: + +- lft and rgt fields +- parent id – the id of the parent node +- level - the level in the hierarchy, level 0 (Clothing in the example above) being the highest +- path + +The path is like the path in a directory structure, and is a join of the alias values going from the root node to the node in question, +with a slash separating the aliases. +For example, referring to the diagram above, if Clothing is the root node, then the path for Jackets would be Men's/Suits/Jackets. + +Notice how the additional fields provide us with efficient mechanisms for navigating the tree. +For instance, we can do a query selecting records WHERE parent is Suits, and ORDER BY lft, +and that will give us Slacks and Jackets in order. +These sorts of operations are generally done on the front end, where we want the site to perform well. +By contrast, admin operations can involve many updates. +For example, if we move Blouses to be the first child under Men's +then it will involve updating the lft and rgt values of nearly all the records in the database table. + +This document describes the library functions with Joomla provides to support managing tree data implemented in a nested set. + +The API is defined at [Joomla Nested APIs](cms-api://classes/Joomla-CMS-Table-Nested.html). + +## Instantiating your table class + +If your extension implements a tree hierarchy in its associated database table, +then you should define your table class to inherit from Joomla\CMS\Table\Nested instead of from Joomla\CMS\Table\Table. +Then you can instantiate an instance in the same way as described in [Basic Table instantiation](../basic-table/#instantiating-your-table-class). + +In the descriptions below, it's assumed that `$table` is a reference to your table instance, eg: + +```php +$table = $this->getTable('example'); // to get the ExampleTable class +``` + +## Root Node + +### Setting the Root Node + +Joomla doesn't provide an API call to set a root node, and the easiest way to accomplish this for a component +is to insert the root node record when you create the database table in your install SQL file. +You should set: + +- parent_id - 0 +- lft - 0 +- rgt - 1 (assuming you're not inserting other nodes at the same time) +- level - 0 +- alias - "root" (for example) +- path - "" + +### Getting the Root Node + +Use `getRootId()`: + +```php +$rootId = $table->getRootId(); +``` + +## Tree nodes and traversal + +### Reading a node + +To retrieve a single node you use the `load()` method, passing the primary key, as usual: + +```php +$table->load($pk); +``` + +The fields of the node record are then available as properties of the `$table` object. + +### Identifying a leaf node + +A leaf node is one that has no child nodes beneath it. +To determine if a node is a leaf node, use code like the following: + +```php +if ($table->isLeaf($pk)) +{ + echo 'This is a leaf node'; +} +else +{ + echo 'This is not a leaf node'; +} +``` + +If you don't specify the `$pk` parameter then the currently loaded record is assumed. + +### Retrieving a Subtree + +To retrieve an entire subtree given the `id` of the base node of the subtree, use code like the following: + +```php +// If $id is the id of a node, retrieve the subtree with this node as its root. +$subtree = $table->getTree($id); +print_r($subtree); +``` + +This will retrieve an array of all the nodes in the subtree. +The array is one-dimensional and lists the nodes in [preorder traversal order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR). +Note that if your table is large then calling `getTree()` from the root node will retrieve the entire table and may cause memory problems. +Use it with caution. + +### Retrieving a Path + +To retrieve all the nodes from the root node along the path leading to a specified node, you can use code like this: + +```php +$pathNodes = $table->getPath($pk); +print_r($pathNodes); +``` + +This will retrieve a one-dimensional array of all the nodes from the root node along the path leading to the node specified by `$pk`. + +## Node CRUD Operations + +### Inserting a node + +Inserting a node is more complex in this case because you need to define where in the tree the new record should be placed. +To do this you call `setLocation()` to specify the location with reference to a node defined in `$referenceId`: + +```php +$table->setLocation($referenceId, $position); +``` + +where `$position` can be: + +- "before" - the new node will be inserted before the reference node but at the same level. +- "after" - the new node will be inserted after the reference node but at the same level. +- "first-child" - the new node will be inserted as the first child node of the reference node. +- "last-child" - the new node will be inserted as the last child node of the reference node. + +After you set the position you insert the record using the same functions as for Joomla Table: + + +```php +$table->setLocation($referenceId, $position); +$table->bind($data); // for an insert the primary key within $data should not already exist in the database table +$table->check(); +$table->store(); +``` + +In order to maintain the integrity of the data structure, the table is locked during a node insertion operation. +Adding a new node to a large table is an expensive operation as the table could potentially be locked for a considerable period. +Take this into account when designing your application. + +### Updating a node + +If you are updating the data of a node but not changing its position in the tree, +then you do this in exactly the same way as you would update a record in a regular database table using the Table class. + +```php +$table->bind($data); +$table->check(); +$table->store(); +``` + +If you are also updating the position of the node in the tree then you must call `setLocation()` first, as described above: + +```php +$table->setLocation($referenceId, $position); +$table->bind($data); +$table->check(); +$table->store(); +``` + +Note that if you're updating the position of a node, and that node has a subtree of children, +then those children will get moved with that node. + +#### Publishing state changes + +There are special considerations for changing the published state of a record, +as you would generally not expect a child node to have a "Published" state if the state of its parent is "Unpublished". +Joomla provides the `publish` method to help with this: + +```php +$table->publish($pks, $state, $userId); +``` + +where: + +- `$pks` is an array of the primary keys of the affected records +- `$state` is the state to be applied - Joomla applies consistency checks to this value +(eg to avoid a node being set to published if one of its parents is unpublished), +and recursively applies this state down through nodes of the subtree below the node. +- `$userid` is the `$userid` of the user performing the change - +if the database table supports checkin/checkout +then Joomla checks that none of the subtree nodes are checked out to other users + +### Deleting a node + +If you delete a node in the tree then you must consider what to do with the child nodes in the subtree below it. + +```php +$table->($pk, true); // deletes the node identified by $pk, and deletes all of its children as well +$table->($pk, false); // deletes the node identified by $pk, and moves all of its children up a level in the tree +``` + +## Rearranging a tree + +### moveByReference + +```php +$table->moveByReference($referenceId, $position, $pk, $recursiveUpdate) +``` + +This moves the node which has primary key `$pk` and all of its children to a new position in the tree. +The new position is specified by the `$referenceId` and `$position`, as described for `setLocation` +in [Inserting a node](./#inserting-a-node) above. + +The API defines the 4th parameter as "Flag indicate that method recursiveUpdatePublishedColumn should be call." +This function will adjust the published state of the moved nodes so that they are not inconsistent with the new parents, +similar to as described in [Publishing state changes](./#publishing-state-changes) above. + +### Order up and Order down + +These functions move a node to the left or right at the same level: + +```php +$table->orderUp($pk); // moves a node one position to the left at the same level +$table->orderDown($pk); // moves a node one position to the right at the same level +``` + +### move + +This is a more generalised function which allows you to move a node to another position at the same level. +Note that you don't specify a `$pk` as a parameter, and the function moves the currently loaded node. + +```php +$table->move($delta, $where); +``` + +The `$where` is a SQL WHERE clause which limits where you are going to move the node. +The `move` method finds the records at the same level which match your WHERE clause. + +If `$delta > 0` then it finds the first record to the right, and moves the currently loaded node to the right of it. + +If `$delta <= 0` then it finds the first record to the left, and moves the currently loaded node to the left of it. + +Despite what the API definition says, the magnitude of `$delta` is irrelevant. \ No newline at end of file From 7f2f64d4b7f70aa0c115394f5a7bd2564cc42a9d Mon Sep 17 00:00:00 2001 From: Robbie Jackson Date: Tue, 25 Nov 2025 22:54:56 +0000 Subject: [PATCH 2/2] fix delete operation --- docs/general-concepts/table/nested.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/general-concepts/table/nested.md b/docs/general-concepts/table/nested.md index 424e42be..940ac56a 100644 --- a/docs/general-concepts/table/nested.md +++ b/docs/general-concepts/table/nested.md @@ -242,8 +242,8 @@ then Joomla checks that none of the subtree nodes are checked out to other users If you delete a node in the tree then you must consider what to do with the child nodes in the subtree below it. ```php -$table->($pk, true); // deletes the node identified by $pk, and deletes all of its children as well -$table->($pk, false); // deletes the node identified by $pk, and moves all of its children up a level in the tree +$table->delete($pk, true); // deletes the node identified by $pk, and deletes all of its children as well +$table->delete($pk, false); // deletes the node identified by $pk, and moves all of its children up a level in the tree ``` ## Rearranging a tree