doctrine / DoctrinePHPCRBundle

This bundle integrates Doctrine PHPCR ODM and PHPCR backends into Symfony
http://phpcr.github.com
MIT License
156 stars 66 forks source link

[intitializer] migration / structure assertion #54

Closed dantleech closed 10 years ago

dantleech commented 11 years ago

It would be neat if I could do something like this

class MyInitiaializer 
{
   public function update()
   {
      $this->assertNodeExists('/cms/content/site_title', function () {
          $block = new StaticContentBlock;
          $block->setContent('...');
          return $block;
      });
   }
}

So if the node exists, we do nothing, if it doesn't then we will create one. This is the general idea anyway.

This would be invaluable when you have a production site and you are adding hard references to content within template etc.

dbu commented 11 years ago

did you see https://github.com/doctrine/DoctrinePHPCRBundle/tree/master/Initializer ? i think that is about what you ask for. or missing something? we need to document that, however.

dantleech commented 11 years ago

Yeah I have seen that (and indeed this is a proposal on top of that), although I am not very familiar with it yet :) I had assumed that this was for initializing the database, and that it should only be executed on a new database.

What I would like is a way to ensure that certain nodes exist. For example, I add several static blocks into my web app on several pages. /cms/content/foo, /cms/content/bar, etc. When I move to production I would have to make a note of all the new static blocks and manually create them, a process which is prone to error and forgetfulness :)

So I would like a way to declare that these blocks should exist and a command that will ensure that they do.

dbu commented 11 years ago

that is exactly what repository:init does. it is save to call it repeatedly. maybe we should document that more clearly - also to increase the chances people implementing initializers understand that :-)

dantleech commented 11 years ago

Maybe we should change the name then? Something like "repository:update" like "doctrine:schema:update --force" (I think thats what its called, its been a long time :) But it worries me a little that the user /could/ break the DB, even if we have clearly documented it.

I like type of methods that I detail above because, if executed in isolation, they are 100% safe (in as far as /we/ handle the update, not the user).

On Sun, Apr 21, 2013 at 02:05:26AM -0700, David Buchmann wrote:

that is exactly what repository:init does. it is save to call it repeatedly. maybe we should document that more clearly - also to increase the chances people implementing initializers understand that :-)

— Reply to this email directly or [1]view it on GitHub.

References

Visible links

  1. https://github.com/doctrine/DoctrinePHPCRBundle/issues/54#issuecomment-16718079
dbu commented 11 years ago

well when creating documents, no matter the name of the method, you have to check if something is already there. if we would say the command knows if a particular service (this is not tied to bundles) was initialized once, we would have to track that state, along with probably some versions. then we are more at the doctrine migrations then the doctrine schema command.

thus i would prefer to keep it simple. i see your point about the connotations init could evoke, but repository:update also sounds weird, especially when running for the first time. lets hear what others say.

dbu commented 11 years ago

what do we do here dan?

dantleech commented 11 years ago

well, what are all the use cases for the "repository:init" method? Is it currently limited to creating nodes which the bundle will need, like /cms/routes?

I'm just wondering if there is a good way to encapsulate the creation of application "structure" nodes, rather than giving the user room to misunderstand / abuse this method. There are also advanced use cases for building CMS structure, for example a multiple site CMS will maybe expect a predefined directory structure beneath the sites node.

Maybe there is even a way to declare that, for example, /cms/{site_x}/routes should exist, and automatically create it when it is requested, or to do something like AutoRouting and automatically create children nodes when a node matching a predefined criteria is created at a predefined path.

But TBH I havn't had any "eureka" moments for this yet :) Some time ago I tried exploring some way of doing something in the way of puppet manifests for the PHPCR-ODM tree:

https://github.com/symfony-cmf/symfony-cmf/wiki/Manifests-for-the-PHPCR-ODM-document-tree

But I gave it up as I couldn't quite see how I could make it work.

dbu commented 11 years ago

to make a new multisite, i would either expect that there would be a service creating all necessary bootstrap, or copying that from a skeleton or such. or if the sites themselves come from a configuration file, the init command could be used to have a cli way to ensure all things are there and not need to check for existence elsewhere.

imho that is enough for now. its not like everybody needs to write the code to generate a new skeleton every day.

lsmith77 commented 11 years ago

I also think that its in many cases easier to write a new initializer service with whatever logic you need instead of coming up with some magic syntax that is smart enough to create the right properties for a specific block type etc.

So lets say we have a multisite setup. I would first create an initializer that ensures that one site exists and another initializer that looks for sites and ensures that their structure is initialized (so maybe we need priorities?). Then adding another site would mostly just be calling a command to add another site and rerunning the initializer command.

As for the command name I dont really see a better option and agree that we just need to make sure to document that initialize should be idempotent.

dantleech commented 10 years ago

I still think some DSL for defining what the structure should be would be great.

$this->node('/cms')->exists();
$this->document('/cms')
    ->class('Acme\BasicCmsBundle\Document\Site')
    ->exists();

The problem with the initializer concept however is that it breaks down when you have a multi-site setup. Site are created dynamically at runtime, and should be "initialized" at runtime - so this feature should be triggered automatically. I think it some analogy to CM systems applies here (Puppet, Chef)

$this->node('/cms')->exists();
$this->document('/cms/site', 'Acme\BasicCmsBundle')->exists()->closure(function ($document) {
    $document->setTitle('Default Site Title');
    $document->setActive(true);
});

// we hook into new object events
$this->withNewDocumentChild('/cms/sites', 'Acme\BasicCmsBundle\Document\Site')
    ->node('./templates')->exists()->end()
    ->node('./articles')->exists()->end()
    ->node('./medias')->exists()->end()

Ignore the fluid interface implementation - I am not excited about writing another fluid interface after QB2 :)

lsmith77 commented 10 years ago

so you are basically talking about sort of an assert API for PHPCR?

dantleech commented 10 years ago

Not sure -- do you mean an assert API à la PHPUnit? Yes and no. One of the things it will do is ensure that a node exists at a given path and if it doesn't create it. The same principle would also apply to Documents.

A more advanced possiblity is that it should be possible for this to replace the functionality provided by RoutingAutoBundle.

dbu commented 10 years ago

is this still relevant? it is labelled "documentation", should we move it to symfony-cmf-docs? though the discussion does not look like its a doc issue.

dantleech commented 10 years ago

No, lets close it. I like the idea, but it is out-of-scope for this bundle.