Under the guidance of berdir I have written up the documentation on how to create a content entity. for D8. The resulting code is a good example for entites in D8.

The module is in the patch (renamed 'content_entity_example'). The documentation will be adapted accordingly.

We will expand the example as D8 evolves.

CommentFileSizeAuthor
#89 interdiff_88.txt13.17 KBmile23
#89 2220565_89.patch33.79 KBmile23
#88 content_entity_example_88.patch31.36 KBcgalli
#88 content_entity_example_84_88.interdiff.txt14.77 KBcgalli
#72 content_entity_example_72.patch25.05 KBcgalli
#72 content_entity_example_56_72.interdiff.txt14.26 KBcgalli
#61 2220565_61.patch24.53 KBmile23
#58 content_entity_example_58.patch0 bytescgalli
#58 content_entity_example_56_58.interdiff.txt1.01 KBcgalli
#56 interdiff.txt1.68 KBmile23
#56 2220565_56.patch24.46 KBmile23
#55 content_entity_example_36_55.interdiff.txt42.74 KBcgalli
#55 content_entity_example_55.patch23.8 KBcgalli
#51 interdiff.txt1015 bytesmile23
#51 2220565_51.patch25.05 KBmile23
#36 content_entity_example_35_36.interdiff.txt8.55 KBcgalli
#36 content_entity_example_36.patch25.05 KBcgalli
#35 content_entity_example_35.patch23.64 KBcgalli
#34 content_entity_example_32_34_interdiff.txt29.41 KBcgalli
#34 content_entity_example_34.patch21.9 KBcgalli
#32 content_entity_example_29_32.interdiff.txt5.5 KBcgalli
#32 content_entity_example_32.patch22.05 KBcgalli
#29 interdiff-2220565.25-29.txt845 bytessocketwench
#29 2220565.29.contentEntityExample.patch22.11 KBsocketwench
#25 interdiff-2220565.22-25.txt6.95 KBsocketwench
#25 2220565.25.contentEntityExample.patch22.11 KBsocketwench
#22 content_entity_example_22.interdiff.txt2.47 KBcgalli
#22 content_entity_example_22.patch21.44 KBcgalli
#20 content_entity_example_20.interdiff.txt4.98 KBcgalli
#20 content_entity_example_20.patch19.95 KBcgalli
#18 content_entity_example_18.interdiff.txt2.39 KBcgalli
#18 content_entity_example_18.patch20.05 KBcgalli
#8 content_entity_example_7.interdiff.txt16.54 KBcgalli
#7 content_entity_example_7.patch19.44 KBcgalli
#3 content_entity_example.patch21.17 KBberdir
content_entity_example.patch21.73 KBcgalli
#82 content_entity_example_72_82.interdiff.txt16.45 KBcgalli
#82 content_entity_example_82.patch31.83 KBcgalli
#84 content_entity_example_72_84.interdiff.txt17.08 KBcgalli
#84 content_entity_example_84.patch31.84 KBcgalli

Comments

Status: Needs review » Needs work

The last submitted patch, content_entity_example.patch, failed testing.

cgalli’s picture

Issue summary: View changes
berdir’s picture

StatusFileSize
new21.17 KB

@cgalli: looks like you managed to create windows file endings again :)

Attaching the same patch, just with unix line endings, converted with dos2unix.

mile23’s picture

Status: Needs work » Needs review

Ossum, thanks. :-)

mile23’s picture

berdir’s picture

Status: Needs review » Needs work
  1. +++ b/content_entity_example/content_entity_example.module
    @@ -0,0 +1,48 @@
    + */
    +function content_entity_example_permission() {
    +  return array(
    

    Permissions are usually readable strings, like administer content_entity_example entity.

  2. +++ b/content_entity_example/lib/Drupal/content_entity_example/ContentEntityExampleInterface.php
    @@ -0,0 +1,48 @@
    +  /**
    +   * Returns the identifier.
    +   *
    +   * @return int
    +   *   The entity identifier.
    +   */
    +  public function id();
    +  /**
    +   * Returns the entity UUID (Universally Unique Identifier).
    +   *
    +   * The UUID is guaranteed to be unique and can be used to identify an entity
    +   * across multiple systems.
    +   *
    +   * @return string
    +   *   The UUID of the entity.
    +   */
    +  public function uuid();
    +  /**
    +   * Return the Value of ContentEntityExample Field.
    +   *
    +   * @return string
    +   *   The content of the field.
    +   */
    +  public function getContentEntityExampleField();
    +  /**
    +   * Defines the base fields of the entity type.
    +   *
    +   * @param string $entity_type
    +   *   Name of the entity type
    +   *
    +   * @return \Drupal\Core\Field\FieldDefinitionInterface[]
    +   *   An array of entity field definitions, keyed by field name.
    +   */
    +  public static function baseFieldDefinitions(EntityTypeInterface $entity_type);
    

    No need to repeat these methods, they exist in the parent. You should however extend from ContentEntityInterface.

    You only want to add method for your own fields, when useful.

  3. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    + *     "translation" = "Drupal\content_translation\ContentTranslationController"
    ...
    + *   translatable = TRUE,
    

    If you actually want to support translations, you should provide a per-translation table. See EntityTestMul for an example. See also the corresponding table definitions. Most of your fields will move to the _field_data table.

  4. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    +    $fields['ceeid'] = FieldDefinition::create('integer')
    +      ->setLabel(t('ID'))
    +      ->setDescription(t('The ID of the ContentEntityExample entity.'))
    +      ->setReadOnly(TRUE);
    

    I would name this just "id". Then you also don't need to override the id() method (will not be necessary anymore after #2182239: Improve ContentEntityBase::id() for better DX anyway)

    The definitions are IMHO betterreadable with an empty line between them.

  5. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    +      ->setPropertyConstraints('value', array('Length' => array('max' => 32)))
    ...
    +        'max_length' => 255,
    +        'text_processing' => 0,
    

    Conflicting length definitions, you only need one of those (max_length easier).

  6. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    +    $fields['type'] = FieldDefinition::create('string')
    +      ->setLabel(t('Type'))
    +      ->setDescription(t('The bundle of the ContentEntityExample entity.'))
    +      ->setRequired(TRUE);
    

    You don't have bundles, so I would leave this out.

    I think it's easier to have an example with bundles and one with, to see the difference. So let's add one without bundles first.

  7. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    +    $fields['user_id'] = FieldDefinition::create('entity_reference')
    +      ->setLabel(t('User ID'))
    +      ->setDescription(t('The ID of the associated user.'))
    +      ->setSettings(array('target_type' => 'user'))
    +      ->setTranslatable(TRUE);
    

    Your entity (the interface) should implement EntityOwnerInterface in this case, then you'll need to implement the corresponding methods (setOwnerId() and so on). Other modules can then rely on that (comment does and entity_reference for example).

    WE should also add changed and created fields, there are special field types for that that just work, see Node.

  8. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -0,0 +1,144 @@
    +    $fields['content_entity_example_field'] = FieldDefinition::create('string')
    +      ->setLabel(t('First ContentEntityExample Field'))
    +      ->setDescription(t('One field of the ContentEntityExample entity.'))
    +      ->setTranslatable(TRUE)
    +      ->setPropertyConstraints('value', array('Length' => array('max' => 32)))
    +      ->setSettings(array(
    +        'default_value' => '',
    +        'max_length' => 255,
    +        'text_processing' => 0,
    +      ))
    +      ->setDisplayOptions('view', array(
    +        'label' => 'Above',
    +        'type' => 'string',
    +        'weight' => -5,
    +      ))
    +      ->setDisplayOptions('form', array(
    +        'type' => 'string',
    +        'weight' => -5,
    +      ))
    +      ->setDisplayConfigurable('form', TRUE)
    +      ->setDisplayConfigurable('view', TRUE);
    +
    +    return $fields;
    

    Not sure how useful this really is?

    Maybe if you use a different field type, like a text field with a text that has a format?

    I would also give it a real name, maybe "description"?

  9. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/Controller/ContentEntityExampleListController.php
    @@ -0,0 +1,38 @@
    +    $row['label'] = l($this->getLabel($entity), 'content-entity-example/' . $entity->id());
    

    Should use \Drupal:l() and the route name instead of the path.

cgalli’s picture

Status: Needs work » Needs review
StatusFileSize
new19.44 KB

New patch included. Yes, I passed it through dos2unix :-)

1) Changed permissions naming. 2 questions remaining:
- The edit and delete permissions work for the node, but the operations column in the list view is not affected.
- The administer permission is not enough to make the admin menus accessible. What am I missing?

2) Removed. The interface is empty but still necessary I believe.

3) Removed translation. Maybe we can implement it in the 'extended' example

4) Renamed to 'id'. I had named it differently to demonstrate the ability of twig to call the id function (as opposed to using the id property)

5) Fixed

6) Removed

7) 'created' and 'changed' fields added. Even in schema.
EntityOwnerInterface implemented. Question: the functions seem to be abstract. Where is this defined?

8) Removed the additional field. It does not add anything that the 'name' field does not already demonstrate.

9) Implemented

cgalli’s picture

StatusFileSize
new16.54 KB

Here comes the interdiff.

berdir’s picture

  1. +++ b/content_entity_example/lib/Drupal/content_entity_example/ContentEntityExampleInterface.php
    @@ -5,44 +5,12 @@
    +interface ContentEntityExampleInterface extends ContentEntityInterface, EntityOwnerInterface {
    

    You can add EntityChangedInterface to the list now.

  2. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -44,51 +43,78 @@ use Drupal\content_entity_example\ContentEntityExampleInterface;
    -    $fields['langcode'] = FieldDefinition::create('language')
    -      ->setLabel(t('Language code'))
    -      ->setDescription(t('The language code of ContentEntityExample entity.'));
    

    Hm, having a langcode is very common for content entities, even if they're not translatable. Fine, though.

  3. +++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/ContentEntityExample.php
    @@ -106,38 +132,18 @@ class ContentEntityExample extends ContentEntityBase implements ContentEntityExa
    -    $fields['user_id'] = FieldDefinition::create('entity_reference')
    +    $fields['uid'] = FieldDefinition::create('entity_reference')
    

    Why this rename? I actually liked user_id more :)

mile23’s picture

You can add EntityChangedInterface to the list now.

It'd be great to have some explanation of each of these interfaces, and what they deliver to the implementation.

+1 on langcode. Seems like a best practice, and best practices are what we're here to explain.

+1 on user_id as well. Descriptive and readable is better.

There was some discussion in email about what to name the entities. I like that the config entity example has 'Robot,' which is distinct and understandable. 'content_entity_example' is the same name as the module, leading to confusion. Let's dream up something that we're modeling here, so that we can then explain how our model maps into an entity implementation.

berdir’s picture

About naming, I agree that something specific would be nicer, the downside is that it then violates the module namespace. But maybe we can solve that by adding a comment on the entity class and state that real entities must live within the namespace of out their module?

Because we'll eventually need 3 different entity types, the idea is to have this as starting point for simple content entities and a more complex one with translatable storage, revisions, bundles (which will need a config entity) and so on. I thought about relying on the config_entity_example module for that, but robots isn't really a good example for that ;)

mile23’s picture

Well my point is that there's presumably a design phase where you discover that you need to make an entity in order to represent something. So, for instance, an address book where you have to represent people and their addresses. This gives the reader something to hang it all on, rather than simply 'this is an example of a content_entity_bundle_thingie_example_no_clue_why_i_need_it.'

The module namespace isn't so relevant as the class namespace. Drupal\content_entity_example\Entity\Robot tells the whole story, and is how sane people should name things anyway. :-)

berdir’s picture

In an actual module, it would still be required to use content_entity_example_robot as the entity type ID. (Just like all other plugins, machine names, routes, ...) Namespaces don't solve that problem. But yes, naming the class like that is perfectly fine.

mile23’s picture

Entity type ID is an arbitrary string, right? So 'content_entity_example.robot' or however you want?

berdir’s picture

No, only lowercase and _ is allowed, and there will be a length limit, although it's not exactly clear yet how long that should be but likely 32 or 50 characters. That string is used for table names for fieldable entities, needs to match variable names so that upcasting works and so on.

We just tried to make that module prefix official and standardized, but it has too many dependencies, see #1862600: Entity type names/IDs are not properly namespaced by owner (e.g., taxonomy.term vs. taxonomy_term).

cgalli’s picture

I like the the idea of putting up some real-life example like an address book or similar. We could do 'person' as the minimal entity and expand into a specific group of people (drupalistas?) as a bundle with all the gimmicks (tranlations, revisions, presentation with twig etc).

Functional comments and tests will be added as well.

As far as name-spacing and conventions to, I am open to your advice.

mile23’s picture

#1862600: Entity type names/IDs are not properly namespaced by owner (e.g., taxonomy.term vs. taxonomy_term) confirms for me why the idea of distributed all-at-once development of complex systems should induce panic attacks. :-)

Anyway, yah, now that I understand the problem, please go with 'content_entity_example_[type]' for the time being.

cgalli’s picture

Correction user_id and language field

Status: Needs review » Needs work

The last submitted patch, 18: content_entity_example_18.patch, failed testing.

cgalli’s picture

Second try, (without .gitignore being included by accident)

Next step: add tests. I waste too much time making sure everything works fine ;-)

cgalli’s picture

Status: Needs work » Needs review
cgalli’s picture

Ok. Started with my first ever test, but somehow I am doing something wrong. The listing link does not seem to appear, so the test fails.

Any ideas?

Status: Needs review » Needs work

The last submitted patch, 22: content_entity_example_22.patch, failed testing.

mile23’s picture

  1. +++ b/content_entity_example/lib/Drupal/content_entity_example/Tests/ContentEntityExampleTest.php
    @@ -0,0 +1,45 @@
    +  /**
    +   * Implements getinfo().
    +   */
    +  public static function getInfo() {
    

    This doesn't actually implement getInfo(). :-) Use {@inheritdoc} instead.

  2. +++ b/content_entity_example/lib/Drupal/content_entity_example/Tests/ContentEntityExampleTest.php
    @@ -0,0 +1,45 @@
    +  public function testContentEntityExample() {
    +
    +    // Listing menu not visible without permission.
    +    $this->assertNoText('Content Entity Example Listing',
    +      'The listing menu appears on the page');
    

    You have to do a drupalGet() before there's content to test.

socketwench’s picture

Status: Needs work » Needs review
StatusFileSize
new22.11 KB
new6.95 KB

Added some stuff based on comments and work on the config entity example. Fixed list builder (nee controller) due to https://drupal.org/node/2200867.

Status: Needs review » Needs work

The last submitted patch, 25: 2220565.25.contentEntityExample.patch, failed testing.

cgalli’s picture

Applied the patch. Now I am getting the error:

Drupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'menu_name' cannot be null: INSERT INTO {menu_links} (menu_name) VALUES (:db_insert_placeholder_0); Array ( [:db_insert_placeholder_0] => ) in Drupal\menu_link\MenuLinkStorageController->save() (line 107 of /var/www/d8/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php).

I belive it has nothing to do with your changes....

cgalli’s picture

+++ b/content_entity_example/lib/Drupal/content_entity_example/Entity/Form/ContentEntityExampleFormController.php
@@ -11,13 +11,15 @@
+  public function builForm(array $form, array &$form_state) {

Typo, I believe. When changed to 'buildForm', it stops working.

socketwench’s picture

Status: Needs work » Needs review
StatusFileSize
new22.11 KB
new845 bytes

Wow. Just. And I thought I manually tested it too.

Status: Needs review » Needs work

The last submitted patch, 29: 2220565.29.contentEntityExample.patch, failed testing.

cgalli’s picture

You did test it. And it probably worked. It just never activated the customized form but used the standard one for the name field.

I am stopping work on this until the DevDays are over. Too many moving targets....

cgalli’s picture

- adapted to changes after DevDays, functional again
- basic tests working, to be expanded

mile23’s picture

Status: Needs work » Needs review
cgalli’s picture

Added autocomplete widget to field definition for user_id
moved structure to PSR-4

cgalli’s picture

StatusFileSize
new23.64 KB

- prepared for entity access controller
- test coverage

cgalli’s picture

Finished access controller
Some style corrections

With this, the basic content entity example is complete

Status: Needs review » Needs work

The last submitted patch, 36: content_entity_example_36.patch, failed testing.

cgalli’s picture

mile23’s picture

mile23’s picture

+++ b/content_entity_example/content_entity_example.module
@@ -0,0 +1,37 @@
+/**
+ * @defgroup content_entity_example Example: Content Entity
+ * @ingroup examples
+ * @{
+ * Example of how to define a content entity.
+ * }
+ */

Still would be great to have some info about what a content entity is, and why you'd use it and so forth. The whole module is kind of sparse on inline comments and other didactic stuff.

Great to have the example, though.

cgalli’s picture

I am currently transforming the example to a more real world example (contacts). In addition, we will include views integration, twig and preprocess as well as a corresponding config entity.

And yes, inline comments will eventually be added as well :-)

mile23’s picture

I'd like to keep these modules as simple as possible. Remember they're more for new people than for someone who wants to re-implement node.

Chunking out some of those other APIs into other example modules would keep things clear, I think.

cgalli’s picture

Ok, we'll keep this in mind.

I think that exposing the data to views belongs here. It's normally one of the first questions that people ask about entities. Twig and the preprocess stuff can be handled by other examples.

berdir’s picture

Yeah, defining an entity involves a number of different API's, so we have to cover them to provide a real example. Views data is useful because that's going to be the common way to display lists of content entities and the views data integration will hopefully be provided by default at some point.

A preprocess and a twig template is currently also necessary, all core entities do that, #2023571: Support preprocessing in EntityViewBuilder would cover the default preprocess stuff while there's no issue seriously trying to add a default template for entities like entity.module in 7.x had.

mile23’s picture

The last submitted patch, 36: content_entity_example_36.patch, failed testing.

mile23’s picture

See the issue for a more complex content entity: #2280723: Create a bundled Content Entity Example for D8

socketwench’s picture

Issue tags: +Needs tests
socketwench’s picture

Issue tags: +Needs PSR-4
mile23’s picture

So I applied #36 and installed it.

Everything seems to work except for deletion... It gives the white screen of death, unable to show me the confirm form.

Also, we don't need hook_schema() any more, thanks to this issue: https://drupal.org/node/2259243

And it would be great if we could stop here for this example and work on the documentation. The goal here isn't to have a complete implementation, but to give people something to read so they can understand how all the pieces interact.

mile23’s picture

Status: Needs work » Needs review
StatusFileSize
new25.05 KB
new1015 bytes

Fixed issue with getCancelRoute() for https://drupal.org/node/2189619

cgalli’s picture

Back after busy times...

I am currently working on renaming and adapting to chances in core (PSR-4, schema etc).

As soon as this is done I will adapt documentation (inline and here: https://drupal.org/node/2192175).

Ok?

Is there a timeline for the example modules to be finished (beta, RC, Final)?

mile23’s picture

Timeline: FINISH NOW! :-)

Examples is always in a dev state; there aren't any releases. The things that matter are: 1) Whether the documentation is accurate, and 2) whether the tests pass.

cgalli’s picture

ok:-) working on it.

cgalli’s picture

Now....

- Changed naming to make it more readable.
- PSR-4
- Schema removed
- Tests included
- Includes change proposed in #51

Please specify the sort of documentation you expect, thanks.

mile23’s picture

StatusFileSize
new24.46 KB
new1.68 KB

Changed menu link name to reference the example module. Added hook_help() to give some context to the contact listing, and a link to the admin page.

Status: Needs review » Needs work

The last submitted patch, 56: 2220565_56.patch, failed testing.

cgalli’s picture

Fixed tests

mile23’s picture

Status: Needs work » Needs review
cameron tod’s picture

The last patch file is empty :)

mile23’s picture

StatusFileSize
new24.53 KB

Woops... Seems like the testbot would fail that. Guess not.

Here's #56 with the interdiff from #58.

mile23’s picture

Still needs a ton of documentation. Look at other examples, especially config_entity_example, to see what's needed.

Basically, Examples are a how-to tutorial that you can run.

mile23’s picture

Status: Needs review » Needs work
cgalli’s picture

will do...

vacho’s picture

When I try to enable the module "content_entity_example" of the #61 patch

This message appear and drupal8 fail

Error

Status message The configuration options have been saved.
The website has encountered an error. Please try again later.

Status: Needs work » Needs review

Mile23 queued 61: 2220565_61.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 61: 2220565_61.patch, failed testing.

mile23’s picture

Ah, more branch blockers. Yay.

cgalli’s picture

Will resume work coming tuesday :-)

Status: Needs work » Needs review

Mile23 queued 61: 2220565_61.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 61: 2220565_61.patch, failed testing.

cgalli’s picture

Issue tags: -Needs tests, -Needs PSR-4
StatusFileSize
new25.05 KB
new14.26 KB

Working again against latest D8.dev.
Tests passing locally.
Removed issue tags

mile23’s picture

Status: Needs work » Needs review

The last submitted patch, 72: content_entity_example_72.patch, failed testing.

cgalli’s picture

All tests are example modules fatal errors.
I am not sure if my code triggers these fails.
Any idea what this could be?

mile23’s picture

Looks like another branch blocker.

Like this: https://twitter.com/socketwench/status/485812535455526913

berdir’s picture

randomName() got renamed to randomMachineName()...

mile23’s picture

Status: Needs review » Needs work

Now that the branch blocker is out of the way... :-)

This is great and I appreciate the effort!

A few things:

  1. +++ b/content_entity_example/content_entity_example.module
    @@ -0,0 +1,62 @@
    \ No newline at end of file
    

    Ewps.

  2. +++ b/content_entity_example/src/ContactAccessController.php
    @@ -0,0 +1,52 @@
    +/**
    + * Access controller for the comment entity.
    + *
    + * @see \Drupal\comment\Entity\Comment.
    + */
    

    I'm sure there's more to say about how an access controller works.

  3. +++ b/content_entity_example/src/ContactInterface.php
    @@ -0,0 +1,18 @@
    +use Drupal\user\EntityOwnerInterface;
    +/**
    + * Provides an interface defining a Contact entity.
    + * @ingroup content_entity_example
    + */
    +interface ContactInterface extends ContentEntityInterface, EntityOwnerInterface {
    

    Formatting error, and also tell me why you create an interface and what it gets used for.

  4. +++ b/content_entity_example/src/Entity/Contact.php
    @@ -0,0 +1,216 @@
    +use Drupal\user\UserInterface;
    +/**
    

    Formatting.

  5. +++ b/content_entity_example/src/Entity/Contact.php
    @@ -0,0 +1,216 @@
    +/**
    + * Defines the ContentEntityExample entity.
    + *
    + * @ingroup content_entity_example
    + *
    + * @ContentEntityType(
    + *   id = "content_entity_example_contact",
    

    Walk us through the annotations and why you set them the way you did.

  6. +++ b/content_entity_example/src/Entity/Contact.php
    @@ -0,0 +1,216 @@
    + *   label = @Translation("ContentEntityExample entity"),
    

    Wouldn't that be 'Contact'?

  7. +++ b/content_entity_example/src/Entity/Contact.php
    @@ -0,0 +1,216 @@
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    

    We really need some explanation of how this fielding system works, and why we need it even though we have all the annotations.

  8. +++ b/content_entity_example/src/Form/ContactSettingsForm.php
    @@ -0,0 +1,55 @@
    +    // Empty implementation of the abstract submit class.
    

    "..abstract submitForm() method."

  9. +++ b/content_entity_example/src/Tests/ContentEntityExampleTest.php
    @@ -0,0 +1,114 @@
    +/**
    + * Class ContentEntityExampleTest.
    + * @package Drupal\content_entity_example\Tests
    + *
    + * @ingroup content_entity_example
    + */
    

    Also @group content_entity_example and @group examples

berdir’s picture

5. It is not possible to add inline comments on annotations. What I'd suggest is a short summary of the interesting parts and then add a reference to \Drupal\Core\Entity\EntityType, where the properties are documented. One important part there is the admin-form thing.

mile23’s picture

That's basically what I meant: A piece in the docblock explaining some of the options and the decision process.

cgalli’s picture

Status: Needs work » Needs review
StatusFileSize
new16.45 KB
new31.83 KB

Checked for style
Added documentation inline

Please check for correctness

Let me know if things are missing

Status: Needs review » Needs work

The last submitted patch, 82: content_entity_example_82.patch, failed testing.

cgalli’s picture

Status: Needs work » Needs review
StatusFileSize
new31.84 KB
new17.08 KB

Fixed tests

The last submitted patch, 84: content_entity_example_84.patch, failed testing.

mile23’s picture

I just fixed the same problem in the branch last night: https://www.drupal.org/node/2200867 "Entity "controllers" are renamed"

mile23’s picture

Getting close... :-)

  1. +++ b/content_entity_example/src/ContactAccessController.php
    @@ -0,0 +1,56 @@
    +use Drupal\Core\Entity\EntityAccessController;
    

    This API changed: https://www.drupal.org/node/2200867

  2. +++ b/content_entity_example/src/Entity/Contact.php
    @@ -0,0 +1,311 @@
    + * Defines the ContentEntityExample entity.
    + *
    + * @ingroup content_entity_example
    + *
    + * This is the main definition of the entity type. From it, an entityType is
    + * derived. The most important properties in this example are:
    + *
    + * - id:          The unique identifier of this entityType. It follows the
    + *                pattern 'moduleName_xyz' to avoid naming conflicts.
    + *
    + * - label:       Human readable name of the entity type.
    + *
    + * - controllers: Controller classes are used for different tasks. You can use
    + *                standard controllers provided by D8 or build your own
    + *                controller, most probably derived from the standard class.
    + *
    + *                view_builder: we use the standard controller to view an
    + *                              instance. It is called when a route lists an
    + *                              '_entity_view' default for the entityType
    + *                              (see routing.yml for details. The view can be
    + *                              manipulated by using the standard drupal tools
    + *                              in the settings.
    

    Thanks for the huge improvement in the docs.

    Just to let you know, though: We can do nested lists in doxy. Otherwise this will get parsed into a bunch of paragraphs. https://www.drupal.org/coding-standards/docs#lists

cgalli’s picture

Hopefully even closer :-)

Simplified help text structure, let's what doxy makes of it.
Adapted to the AcessControlHandler API changes.

Tests pass locally against the latest D8 pull....

mile23’s picture

StatusFileSize
new33.79 KB
new13.17 KB

Moved hook_help() content to the list builder.

Added some tests for the paths.

I'm not really sure how we're supposed to be using @package, so I left that in.

Fixed a few formatting errors.

mile23’s picture

Status: Needs review » Fixed

And with that... We're done!

Thanks, everyone, but especially cgalli for sticking with it. :-)

  • Mile23 committed 879d86e on 8.x-1.x authored by cgalli
    Issue #2220565 by cgalli, Mile23, socketwench, Berdir: Added Content...
cgalli’s picture

Yay :-)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

kpv’s picture

Status: Closed (fixed) » Needs work

It seems that Views integration should be added here since all core content entities do.

berdir’s picture

Status: Needs work » Closed (fixed)

views integration was postponed on the EntityViewsData handler to land, which is being worked on now: #1740492: Implement a default entity views data handler

And we should do that in a new issue I think.

kpv’s picture

Status: Closed (fixed) » Needs work

There was a change in entity annotation: Entity controller admin-form annotation replaced by field_ui_base_route
Should we post required changes into the current issue or open new issues when core api changes happen (like this one)?

mile23’s picture

Title: Content Entity Example » Create a Content Entity Example
Status: Needs work » Closed (fixed)

New issues, please.

Thanks.