Hi all,
I'm using PHPCR with Jackalope/Jackrabbit.
While messing around with fixtures and cloning across workspaces, I forgot to purge and managed to get myself some same name siblings (SNS):
/cr
/cr[2]
/cr[3]
etc.
So, the problem: given the above, PHPCRPurger->purge() results in a PHPCR\PathNotFoundException (via Jackrabbit HTTP 409) for node /cr[n] (where 'n' is around halfway down the list).
I thought this might be reasonably simple to solve, but then I started digging around... ;)
The problem can be observed in Jackalope\Transport\Jackrabbit\Client::finishSave() [1], although this method is as much the messenger as the culprit. In fact, I eventually came across a post on the jackrabbit-dev list by Christian Stocker from just over a year ago [2], so it seems I wasn't the first to come across this.
Here's an example of the string the Jackalope-Jackrabbit transport sends in my failing case:
:diff=-/cr : \r-/cr[2] : \r-/cr[3] :
If I hard-wire that for a moment, but in reverse, i.e.
:diff=-/crr[3] : \r-/cr[2] : \r-/cr :
- then it works!
So, a bit of fiddly string manipulation and we're done, right? Well, when I looked at the transport client, there are a number of possible operations, with these prefixes:
Delete: -
Move: >
Store property: ^
Create: +
(I might have missed/misunderstood some).
If there are only deletes, as with the purge case, then just changing the order is ok. But what about a mixture of deletes, moves, creates etc? In the mailing list exchange mentioned above, it was suggested that this possibly should be handled on the server side. A Jira issue was mentioned but I couldn't find this.
So should we attempt to resolve the order of operations in the transport, before sending the request? Or even in the object manager's save() method? I think this will be very difficult to get right in all cases though.
I'm not sure if it's related, but I've had similar problems in the the past with a form that allowed renaming multiple nested blocks. To get this working I had to carefully order things around in my own code, and perform a manager->flush() part-way through.
For the purging use case though, I think the expected behaviour would be that it will always result in an empty workspace. Perhaps a work-around for PHPCRPurger is in order?
For example, add a 'deep' option (or similar):
public function purge($deep = true)
and
purgeWorkspace(SessionInterface $session, $deep = true)
This latter function would then do a session->save() and a fresh $root->getNodes() each time (perhaps only if SNS are detected). Probably not good for performance, but only significant if the root node has a lot of SNS children.
Failing that, it's not too hard to do effectively the same thing in my own application's fixtures loader/purger, but I suspect this will affect other people too.
Hoping this all makes sense!
Thanks,
Lars.