Files
nextcloud-docs/developer_manual/digging_deeper/webdav_collection_preload.rst
2025-09-02 10:36:11 +02:00

97 lines
3.5 KiB
ReStructuredText

.. _collection_preload:
=================================
WebDAV collection preload events
=================================
Overview
--------
During a WebDAV PROPFIND on a SabreDAV collection with ``Depth > 0``,
Nextcloud emits a preload event so DAV plugins can fetch data for the
collection's children in one go. The goal is to avoid N+1 database queries by
preloading what your property handlers need before per-node property handling
starts. In practice, this means your plugin can fill a cache up front, then
read from it in the usual ``propFind`` handlers for a faster and more
efficient response.
When the event is emitted
-------------------------
The event is emitted during PROPFIND requests, before the ``propFind`` event,
for nodes implementing ``Sabre\DAV\ICollection`` with ``Depth > 0``.
The event may be emitted multiple times for a given request path; plugins should
check their caches to avoid duplicate work.
Subscribing to the event in a plugin
------------------------------------
Register a listener in your implementation of Sabre's ``ServerPlugin::initialize()`` method:
.. code-block:: php
use Sabre\DAV\ICollection;
use Sabre\DAV\PropFind;
use Sabre\DAV\ServerPlugin;
class MyDavPlugin extends ServerPlugin {
public function initialize(\Sabre\DAV\Server $server): void {
// Called before per-node property handlers
$server->on('preloadCollection', $this->preloadCollection(...));
// Your usual property handlers
$server->on('propFind', $this->handleGetProperties(...));
}
private function preloadCollection(PropFind $propFind, ICollection $collection): void {
// Only preload when your properties were actually requested
$requested = [
'{http://appdomain.example/ns}your-prop',
'{http://appdomain.example/ns}another-prop',
];
$anyRequested = array_reduce(
$requested,
fn($result, $property) => $result || $propFind->getStatus($property) !== null,
false
);
if (!$anyRequested) {
return;
}
// Fetch data for the collection and its children in bulk
// and cache results for use in your propFind handler
$this->cache = $this->bulkLoadDataForCollection($collection); // implement your own caching
}
private function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node): void {
// Read and return values from $this->cache to avoid per-node queries
// Handle per-node property loading here to support Depth = 0
// and cases where the 'preloadCollection' event is not emitted.
}
}
Built-in examples
-----------------
- Tags: ``OCA\DAV\Connector\Sabre\TagsPlugin`` preloads tags and favorite
info for a folder and its children.
- Shares: ``OCA\DAV\Connector\Sabre\SharesPlugin`` preloads share types and
sharees for items within a folder.
- Comments: ``OCA\DAV\Connector\Sabre\CommentPropertiesPlugin`` preloads unread
comment counts for items within a folder.
Best practices
--------------
- Check requested properties: Use ``$propFind->getStatus('{ns}property')`` to
confirm that your properties were actually requested before querying.
- Cache results: The event can fire multiple times; cache by file ID or path
to avoid redundant work during the same request.
- Scope your preload: Only fetch data for the current collection and (at most)
its direct children; avoid fetching across the whole tree.