Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support child restriction #706

Merged
merged 1 commit into from
Jun 20, 2016
Merged

Support child restriction #706

merged 1 commit into from
Jun 20, 2016

Conversation

dantleech
Copy link
Contributor

@dantleech dantleech commented May 30, 2016

TODO

  • Wrap up feature
  • Documentation (also mention that if a document already is there because the situation was created before the annotation was used or from raw phpcr operations, the child will be on the document as validation only happens during save but not on read)

Doc PR: doctrine/phpcr-odm-documentation#81

This PR allows the restriction of child document types:

/**
 * @PHPCRODM\Document(restrictChildren={
 *   "Doctrine\Tests\Models\CMS\CmsArticle"
 * })
 */
 class ArticleFolder
 {
     // ...
 }
  • By default children are unrestricted (childRestrictions = null)
  • Children validated on "insert".
  • Children validated on "move".

Why?

  • Ensure structural integrity where it matters.
  • Determine which types can be added to a document (e.g. in a tree browser).
  • Ultimately this could also be part of the node type definition if we ever implementing that.

Alternatives

We could also implement this as a separate annotation:

/**
 * @Document()
 * @RestrictChildren({
 *   "AppBundle\Document\Foo"
 *   "AppBundle\Document\Bar"
 * }, restrict="include")
 */

Where restrict is an enum of none, include and exclude.

if ($node->hasProperty('phpcr:class')) {
$nodeClassName = $node->getProperty('phpcr:class')->getString();
if ($node->hasProperty(self::CLASS_PROPERTY)) {
$nodeClassName = $node->getProperty(self::CLASS_PROPERTY)->getString();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grep to see if phpcr:class is referenced anywhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this is too risky (adding class constants to places that might be untested) and unrequired for this PR, reverting.

Copy link
Member

@dbu dbu Jun 3, 2016 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dantleech
Copy link
Contributor Author

Would need to add XML and YAML drivers.

@dbu
Copy link
Member

dbu commented May 31, 2016

interesting proposal! indeed the outside tree definition we currently have is awkward and weak. and i always forget to add my new classes to it.

i wonder if we should do this on the class level or on Child / Children mappings and then merge all Child / Children mappings to know the allowed children. or maybe keep what you propose, but add that when the annotation / attribute is present the child and children types are merged into the allowed list automatically. then i can potentially limit my model by having children explicitly mapped and just set an empty annotation / attribute to trigger restriction. i guess restricting by default is not that user friendly (and a huge BC break that can be annoying even when upgrading to 2.0 at some point)

i think i prefer the separate annotation, given that we could have options. it could be something like

/**
 * @Document()
 * @RestrictChildren(
 *    include={"AppBundle\Model\MarkerInterface"}, 
 *    exclude={"AppBundle\Document\SpecialCase"}
 * )
 */

@dantleech
Copy link
Contributor Author

i wonder if we should do this on the class level or on Child / Children mappings and then merge all Child / Children mappings to know the allowed children

I did think about doing it on the Children mapping - but that applies to a property (i.e. there are many properties even if you can only map one as having the Children) - you might not even want to map the children to a property.

I think its correct to say "this document can have these children" rather than "this document which has a property mapped as children wiith these types can have those children".

i guess restricting by default is not that user friendly (and a huge BC break that can be annoying even when upgrading to 2.0 at some point)

If we add an extra annotation then I suggest that the Metadata has an enum $childRestriction with the values none, inclusive and exclusive, with the default being none.

So the annotation would be fully expressed:

 * @RestrictChildren(
 *    classes={"AppBundle\Model\MarkerInterface"}, 
 *    type="include"
 * )

If classes are present then we could automatically switch from none to include.

@dbu
Copy link
Member

dbu commented Jun 1, 2016

i prefer to have the explicit lists (see the example i created) as it allows you to allow everything of a base class but exclude some exceptions. like the sonata admin extensions do. an empty @RestrictChildren would say there can be no children.

and i would merge types from Child / Children mappings into the allowed children. otherwise you need to duplicate that. there can be several Children annotations, btw. you can restrict them to prefixes and use a naming pattern to separate children. avoids you to have Child with a container object.

@dantleech
Copy link
Contributor Author

I prefer to have the explicit lists (see the example i created) as it allows you to allow everything of a base class but exclude some exceptions.

ok thats makes sense.

and i would merge types from Child / Children mappings into the allowed children. otherwise you need to duplicate that.

I don't think you can set type for Child / Children? only a node-name filter?

@dbu
Copy link
Member

dbu commented Jun 2, 2016 via email

*
* @var array
*/
public $childRestrictions = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

child or children? not sure which is the correct way. its the restriction on each child...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we were to follow from PHPCR we could just have requiredClasses as with NodeDefinition::getRequiredPrimaryTypes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also interesting that they do not permit blacklisting (afaict)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But maybe this is an argument to keep it as a whitelist - as if we do allow mapping by phpcr_type in the future, this feature would not map to it.

Copy link
Member

@dbu dbu Jun 2, 2016 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we go with a requiredClasses attribute or a separate annotation?

Copy link
Member

@dbu dbu Jun 3, 2016 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbu dbu changed the title Support child restriction WIP: Support child restriction Jun 2, 2016
@dbu
Copy link
Member

dbu commented Jun 2, 2016

what if i edit a document that is in an illegal place already, but is neither moved nor created? i think it would make sense to allow that - a user otherwise could have a hard time saving their changes and understanding what is happening. and the situation was illegal before the edit already...

@dantleech
Copy link
Contributor Author

what if i edit a document that is in an illegal place already

Not sure .. if their tree is in an illegal state, then its broken, so I would expect an \Exception when editing an illegal child - otherwise nobody knows that it is broken until part of the system that relies on chldren being of a specific type recieves a class of the wrong type and throws an error at a critical moment.

But it does raise the issue of migration when restrictions are added to an existing model - but I would say that that excerise could be left to the user with the PhpcrMigrationsBundle.

@dbu
Copy link
Member

dbu commented Jun 2, 2016 via email

@dantleech
Copy link
Contributor Author

we could do a command to validate the validity of the current tree, to
help figure out if the migration worked as expected...

yeah that crossed my mind too.. I would say that could be another PR.

@dbu
Copy link
Member

dbu commented Jun 2, 2016

we could do a command to validate the validity of the current tree, to
help figure out if the migration worked as expected...

yeah that crossed my mind too.. I would say that could be another PR.

yes, for sure. there are other things to check, like missing required
properties.

@dantleech
Copy link
Contributor Author

dantleech commented Jun 3, 2016

@dbu have added the XML and YAML drivers and tests.

So it looks like this:

Annotation:

@Document(requiredClasses={"stdClass"})

XML:

<document ...>
    <required-class>stdClass</required-class>
    <!-- ... -->
</document>

YAML:

MyMapping:
    required_classes: [ "stdClass" ]

There remains the case where the user wants to explicitly declare that they want no children, which is not possible the XML or YAML drivers, we could add a "allow_children" boolean flag?

MyMapping:
    allow_children: false

@dantleech
Copy link
Contributor Author

dantleech commented Jun 3, 2016

Made the benchmark, no noticable difference:

+------------------+--------------------------------------+------------------------+
| subject          | vcs_branch:children_restriction:mean | vcs_branch:master:mean |
+------------------+--------------------------------------+------------------------+
| benchPersistTwo  | 40.43ms                              | 39.67ms                |
| benchPersistMany | 65.65ms                              | 72.44ms                |
+------------------+--------------------------------------+------------------------+
vcs_branch: master
+------------------+-----+-------------+---------+---------+--------+
| subject          | its | mem         | mean    | mode    | rstdev |
+------------------+-----+-------------+---------+---------+--------+
| benchPersistTwo  | 10  | 11,248,728b | 39.67ms | 39.03ms | 11.71% |
| benchPersistMany | 10  | 11,403,322b | 72.44ms | 66.97ms | 15.04% |
+------------------+-----+-------------+---------+---------+--------+

vcs_branch: children_restriction
+------------------+-----+-------------+---------+---------+--------+
| subject          | its | mem         | mean    | mode    | rstdev |
+------------------+-----+-------------+---------+---------+--------+
| benchPersistTwo  | 10  | 11,264,174b | 40.43ms | 36.61ms | 17.80% |
| benchPersistMany | 10  | 11,419,214b | 65.65ms | 62.13ms | 9.41%  |
+------------------+-----+-------------+---------+---------+--------+

It uses a bit more memory however.

@dbu
Copy link
Member

dbu commented Jun 3, 2016

i don't like @Document(requiredClasses={...}). it feels like we would define the classes this document may have, which would make no sense but could be confusing. i think allowedChildClasses would be better.

in jackrabbit cnd the child types are defined on the child / children names. i think we have the same here. on an @Child/Children annotation, we don't need to repeat its about the children, but on the class level we should. requiredChildClasses feels like the children has to implement all of the classes, or we require children, but what we do is only allow children that are of one of the specified classes.

@dbu
Copy link
Member

dbu commented Jun 3, 2016

and thanks for the benchmark, looks like its not a noticable performance decrease. and its for write only, not for read so it should be pretty safe.

@dbu
Copy link
Member

dbu commented Jun 3, 2016

regarding forbidding all children: what if we allow to just have one entry with false and interpret that as not allowing children? in xml, it would be <allowed-child-class>false</...> which feels ok-ish. if you don't like it, a separate option would be ok. they would need to be sanity checked as the two things are mutually exclusive.

@dantleech
Copy link
Contributor Author

and go with requiredClasses - i like to keep this as close to PHPCR as makes sense.

i don't like @document(requiredClasses={...})

:) but also fine to change it, its just for the sake of copying PHPCR.

@dantleech
Copy link
Contributor Author

I think I prefer the explcit option, will update.

@@ -1,9 +1,12 @@
<phpunit colors="true" bootstrap="bootstrap.php" backupGlobals="false" cacheTokens="true">
<php>
<env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May as well remove this

@dantleech dantleech force-pushed the children_restriction branch 2 times, most recently from 217299b to 3cde352 Compare June 16, 2016 07:58
@dbu
Copy link
Member

dbu commented Jun 18, 2016

can you please rebase on master and then bump the branch-alias for master to 1.4? i think apart from that we are pretty much ready.

@dantleech dantleech force-pushed the children_restriction branch 2 times, most recently from 7390b49 to ad381e0 Compare June 18, 2016 09:16
@dantleech
Copy link
Contributor Author

Rebased and bumped the branch alias for this branch to 1.4-dev


$childClasses = $this->getChildClasses();

if (array() === $childClasses) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (0 === count($childClasses)) { ?

@dantleech dantleech force-pushed the children_restriction branch 2 times, most recently from dac5d49 to 9329d4b Compare June 18, 2016 15:04
@dantleech
Copy link
Contributor Author

@dbu good to merge?

$cm->setChildClasses(array('stdClass'));
$childCm = new ClassMetadata('stdClass');
$childCm->initializeReflection(new RuntimeReflectionService());
$result = $cm->assertValidChildClass($childCm);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd even remove the assignment here and just have $cm->assertValidChildClass($childCm);

@dbu
Copy link
Member

dbu commented Jun 20, 2016

i'd prefer to clean up that assertNull in the tests. apart from that its ready, yes. great job!

@dbu dbu changed the title WIP: Support child restriction Support child restriction Jun 20, 2016
@dantleech
Copy link
Contributor Author

ok, well if it could cause a compiler to crash then its not good :) removed the assertNull checks on the validation tests.

@dbu dbu merged commit 23a0c6a into master Jun 20, 2016
@dbu dbu deleted the children_restriction branch June 20, 2016 12:12
@lsmith77 lsmith77 removed the wip/poc label Jun 20, 2016
@dbu
Copy link
Member

dbu commented Jun 20, 2016

yay!

@dantleech
Copy link
Contributor Author

\o/

On Mon, Jun 20, 2016 at 05:12:43AM -0700, David Buchmann wrote:

yay!


You are receiving this because you were mentioned.
Reply to this email directly, [1]view it on GitHub, or [2]mute the thread.

Reverse link: [3]unknown

References

Visible links

  1. Support child restriction #706 (comment)
  2. https://github.com/notifications/unsubscribe/AAgZcSlRi9klSdpQ-3NlTI8ul6LPb6_vks5qNoO7gaJpZM4Ipo4b
  3. Support child restriction #706 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants