drupal http://andrejgaluf.com/tags/drupal en Drupal 7: Setting title module field programmatically http://andrejgaluf.com/blog/2015-11-27/drupal-7-setting-title-module-field-programmatically <div data-history-node-id="10" class="node node--type-blog-post node--view-mode-rss ds-1col clearfix"> <div class="field field--name-dynamic-token-fieldnode-custom-submitted field--type-ds field--label-hidden field__item"><p>Submitted by <a href="http://andrejgaluf.com/users/admin">Andrej Galuf</a> on 27. November 2015.</p> </div> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Hello and welcome back to Drupal Tips and Tricks. Today we will be looking at the <a href="https://www.drupal.org/project/title">Title</a> module in combination with <a href="https://www.drupal.org/project/entity_translation">Entity Translation</a> and we will be trying to change the value of the title field programmatically.</p> <p>Title module replaces the title property on an entity with a field that can hold different language values. Unfortunately, this isn't as simple as it sounds, because the core title property is defined and used by the core directly in the node table.</p> <p>As a result, there are some unforseen side effects, such as problems when saving the value outside the dedicated code, for instance programmatically or from a custom form submit.</p> <p>Let's look at one such example submit:</p> <pre> <code class="language-php">&lt;?php $wrapper = entity_metadata_wrapper('node', $nid); $wrapper-&gt;language('en')-&gt;title_field-&gt;set('Test'); $wrapper-&gt;save();</code></pre> <p>Unfortunately, the above code does not work as expected. If you're lucky, this code won't do anything - the title value won't change. If you're unlucky, the core will break your titles completely (for instance, set a title in original language rather than translation).</p> <p>How can we fix this?</p> <p>First, you'll need the <a href="https://www.drupal.org/node/2267251">patch #27 here</a>. Copy it to the title module folder and apply as usual (patch -p1 &lt; 2267251-27.patch). Now, let's try again:</p> <pre> <code class="language-php">&lt;?php // Node language is 'en' $wrapper = entity_metadata_wrapper('node', $nid); $wrapper-&gt;language('en')-&gt;title_field-&gt;set('English Title'); $wrapper-&gt;language('de')-&gt;title_field-&gt;set('German Title'); $wrapper-&gt;save();</code></pre> <p>Can you guess what this code will do? It will save the german title, but not the english one.</p> <p>... what?</p> <p>Title module makes sure that the title_field in original language and node title are syncronized at all times. Unfortunately, the code ignores the value in the original language when set through the title_field. Here's the correct way to save the original and translated title value:</p> <pre> <code class="language-php">&lt;?php // Node language is 'en' $wrapper = entity_metadata_wrapper('node', $nid); $wrapper-&gt;title-&gt;set('English Title'); $wrapper-&gt;language('de')-&gt;title_field-&gt;set('German Title'); $wrapper-&gt;save();</code></pre> <p>Hopefully, this will save you some debugging time.</p> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-inline"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/tags/quick-tips" hreflang="en">quick tips</a></div> <div class="field__item"><a href="/tags/drupal" hreflang="en">drupal</a></div> <div class="field__item"><a href="/tags/drupal-7" hreflang="en">drupal 7</a></div> </div> </div> <div class="field field--name-dynamic-token-fieldnode-comments-title field--type-ds field--label-hidden field__item"><span>Comments</span> </div> <section class="field field--name-comment field--type-comment field--label-hidden comment-wrapper"> <a id="comment-37"></a> <article data-comment-user-id="0" about="/comment/37" typeof="schema:Comment" class="comment js-comment by-anonymous"> <mark class="hidden" data-comment-timestamp="1485641907"></mark> <footer class="comment__meta"> <article typeof="schema:Person" about="/user/0" class="profile"> </article> <p class="comment__submitted"><span rel="schema:author">Submitted by <span lang="" typeof="schema:Person" property="schema:name" datatype="">d70rr3s (not verified)</span> on Thu, 01/26/2017 - 10:41</span> <span property="schema:dateCreated" content="2017-01-26T09:41:35+00:00" class="rdf-meta hidden"></span> </p> <a href="/comment/37#comment-37" hreflang="en">Permalink</a> </footer> <div class="content"> <h3 property="schema:name" datatype=""><a href="/comment/37#comment-37" class="permalink" rel="bookmark" hreflang="en">You save the day</a></h3> <div property="schema:text" class="clearfix text-formatted field field--name-comment-body field--type-text-long field--label-hidden field__item"><p>Thanks a lot! I was driving mad with this issue. I have a custom web service for pushing nodes to my site and thus I was receiving and set in right (well I thought was the right way to do it) on save the title_field value was set back to null. Anyway thanks again and great tip, should be included on Title module docs.</p> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=37&amp;1=default&amp;2=en&amp;3=" token="aa5h27Zf_Io7eS7_oKymx0W8fnjVh-hWQNuWQ3Ciw78"></drupal-render-placeholder> </div> </article> <h2 class="title comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=10&amp;2=comment&amp;3=comment" token="YckVX30_aqr-xobBuxYh1_EWuyROHe8PdCJuREjuTIY"></drupal-render-placeholder> </section> </div> Fri, 27 Nov 2015 17:39:30 +0000 admin 10 at http://andrejgaluf.com A quick introduction to Drupal Entities http://andrejgaluf.com/blog/2015-11-25/a-quick-introduction-to-drupal-entities <div data-history-node-id="9" class="node node--type-blog-post node--view-mode-rss ds-1col clearfix"> <div class="field field--name-dynamic-token-fieldnode-custom-submitted field--type-ds field--label-hidden field__item"><p>Submitted by <a href="http://andrejgaluf.com/users/admin">Andrej Galuf</a> on 25. November 2015.</p> </div> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Drupal has always been known for its immense flexibility and exceptional adaptability. The CCK module's field system was a large step in the flexibility direction, but it had its limits. The fields were only really usable on content types (read: nodes), leading to additional modules which extended profiles, provided forms, a shopping cart, forums and more. But in Drupal 7, a new unified API, the <strong>entity system</strong>, was introduced that aimed to unify the various elements of the site - and it changed everything.</p> <p>This unified API would now be responsible for accessing elements such as content types, users, taxonomies and comments. The first visible advantage of the entity system was that the fields could now easily be attached to any of them, further extending Drupal's flexibility. However, the entities proved to be a lot more than that. Suddenly, it wouldn't take a lot of code anymore to turn just about any custom table into a full-fledged entity, with all its advantages. With a couple of functions, entities would integrate with Views, Rules and similar complex modules, allowing for unmatched flexibility.</p> <p>Since Drupal 7's launch, the entity system has been used for everything from field-based replacements for previously separate module systems (for example <a href="https://www.drupal.org/project/entityform">EntityForm</a> as a replacement for <a href="https://www.drupal.org/project/webform">Webform</a>) to massive extensions that turned previously limited elements into world-class tools (for example <a href="https://www.drupal.org/project/commerce">Drupal Commerce</a>). It's no wonder that Drupal 8 is even more entity-oriented and introduces a large amount of previously contributed modules into the core.</p> <p>I will be referencing entites a lot in the future, so it makes sense to look at them first. We will be taking a quick peek into the recent past (Drupal 7), dive into an entity system of Drupal 8 and learn how to best proceed if you want to use them today.</p> <h3>What is an entity?</h3> <p>Entites were the Drupal's first serious step towards an object oriented future. Drupal had several elements such as nodes, taxonomies and users doing more or less the same thing a little differently. This ment that for instance CCK allowed the creation of fields for content types, but couldn't do the same for users. To simplify and unify the underlying code that would allow this, the entity API was introduced. In terms of object oriented development, think of them as an abstraction of functionality common between nodes, taxonomies and users, a base class that others extend.</p> <p>However, an entity is more than that. Where previously (in Drupal 6), one could use nodes to represent 99% of the content, this presented a significant overhead and caused performance issues. Starting with Drupal 7, one could now build a new entity that would be custom tailored to the task at hand. This role only increased in Drupal 8, where entities are now used not just for content, but for configurations as well.</p> <h3>Creating a custom entity in Drupal 7</h3> <p>In order to create an entity in Drupal 7, one first needed a base table, defined by schema in - surprise! - <strong>hook_schema</strong>. Once the base table was available, one would specify a <strong>hook_entity_info</strong> that contained the entity name, base table, keys, settings and controllers. Depending on the complexity, custom controllers and callbacks could be specified, but <a href="https://www.drupal.org/project/entity">Entity API</a> module - which you needed anyway for <em>EntityMetadataWrapper</em> - provided reasonable defaults for simple entities. Unfortunately, even if the base table was already defined by hook_schema, one still needed to use <strong>hook_entity_property_info</strong> to specify properties of the entity, leading to separation of related code. The hook_entity_property_info had a bit of a catch for beginners too - as long as one didn't define a single setter or getter callback, the code would assume defaults and they would all we available to modules such as views. However, as soon as a single callback was specified, one would only have access to the specified property. Not that big a deal, but it's a bit of a gotcha when you first face it.</p> <p>Apart from this, not much was needed. The usual assortment of <strong>hook_menu</strong> options would do, though you would generally extend the default Entity class to specify a custom URI.</p> <p>Here's a quick look at bare minimum Drupal 7 entity code:</p> <pre> <code class="language-php">&lt;?php // This comes in MYMODULE.install /** * Implements hook_schema */ function MYMODULE_schema() { $schema = array(); $schema['hello_world'] = array( 'description' =&gt; t('An entity containing hello world'), 'fields' =&gt; array( 'id' =&gt; array( 'description' =&gt; 'Hello World ID', 'type' =&gt; 'serial', 'not null' =&gt; TRUE, 'unsigned' =&gt; TRUE, ), 'entity_id' =&gt; array( 'type' =&gt; 'int', 'description' =&gt; 'Related node id', 'unsigned' =&gt; TRUE, 'not null' =&gt; TRUE, ), 'label' =&gt; array( 'type' =&gt; 'varchar', 'not null' =&gt; TRUE, 'length' =&gt; 50, 'default' =&gt; '', ), 'value' =&gt; array( 'type' =&gt; 'varchar', 'not null' =&gt; FALSE, 'length' =&gt; 255, 'default' =&gt; NULL, ), ), 'primary key' =&gt; array('id'), 'foreign keys' =&gt; array( 'node' =&gt; array( 'table' =&gt; 'node', 'columns' =&gt; array('entity_id' =&gt; 'nid'), ), ), ); return $schema; } // This comes in MYMODULE.module /** * Implements hook_entity_info(). */ function MYMODULE_entity_info() { $info = array(); $info['hello_word'] = array( 'label' =&gt; t('Hello World'), 'base table' =&gt; 'hello_world', 'entity keys' =&gt; array( 'id' =&gt; 'id', 'label' =&gt; 'label', ), 'module' =&gt; 'MYMODULE', // The following two classes are needed by EntityAPI module 'entity class' =&gt; 'MyEntity', 'controller class' =&gt; 'EntityAPIController', // And for views 'views controller class' =&gt; 'EntityDefaultViewsController', // Needed for admin interface 'access callback' =&gt; 'my_custom_access_callback', 'uri callback' =&gt; 'entity_class_uri', 'admin ui' =&gt; array( 'path' =&gt; 'admin/structure/hello', 'controller class' =&gt; 'EntityDefaultUIController', ), 'plural label' =&gt; 'Hello World', // This is needed for fields 'fieldable' =&gt; TRUE, 'bundles' =&gt; array( 'hello_world' =&gt; array( 'label' =&gt; t('Hello World'), 'admin' =&gt; array( 'path' =&gt; 'admin/structure/hello', ), ), ), ); return $info; } /** * Implements hook_entity_property_info(). */ function MYMODULE_entity_property_info() { $info = array(); $info['hello_world']['properties']['id'] = array( 'type' =&gt; 'integer', 'label' =&gt; t('Hello World ID'), 'description' =&gt; t('The ID of the Hello World Entity'), 'schema field' =&gt; 'id', ); $info['hello_world']['properties']['entity_id'] = array( 'type' =&gt; 'node', 'label' =&gt; t('Node ID'), 'description' =&gt; t('ID of the related node'), 'schema field' =&gt; 'entity_id', 'setter callback' =&gt; 'entity_property_verbatim_set', 'getter callback' =&gt; 'entity_property_verbatim_get', ); $info['hello_world']['properties']['label'] = array( 'type' =&gt; 'text', 'label' =&gt; t('Label'), 'description' =&gt; t('Hello World Label'), 'schema field' =&gt; 'label', 'setter callback' =&gt; 'entity_property_verbatim_set', 'getter callback' =&gt; 'entity_property_verbatim_get', ); $info['hello_world']['properties']['value'] = array( 'type' =&gt; 'text', 'label' =&gt; t('Value'), 'description' =&gt; t('Hello World Value'), 'schema field' =&gt; 'value', 'setter callback' =&gt; 'entity_property_verbatim_set', 'getter callback' =&gt; 'entity_property_verbatim_get', ); return $info; }</code></pre> <p>As you can see, it's all really straightforward and not that much code, yet this is all it takes to have Rules, Views and Drupal's general entity tools fall in line and recognize your lowly table as one of their mighty entities. As soon as you have this, you can add fields to your entity, you can have views create queries and recognize relationships (in our case a node) and you can use EntityFieldQuery and EntityMetadataWrapper to set or get your properties or field values.</p> <h3>Entity system in Drupal 8</h3> <p>It's no wonder then that the importance of Entity system only grew in Drupal 8. Where in Drupal 7, the entities were little more than an afterthought and built upon after the CMS already launched, Drupal 8 is built all around them. Entities were further decoupled and split into two general subtypes - the content and the configuration entities. Don't worry, thanks to the awesome Symfony 2 base and object oriented programming, you can easily add more. But much like the CMS itself, the entities can get a little daunting at first.</p> <p>Simple things first - hook_menu is gone, replaced by a routing system split into a plethora of .yml files, where you can find menu links, tasks, actions. Entity is defined in a class that's located within the src/Entity subfolder of the module. hook_entity_info is now a simple annotation of the class, as per Symfony 2 standards, and hook_entity_property_info is a method <strong>baseFieldDefinitions</strong> on the Entity's class. Wait a moment, that's all our hooks from Drupal 7 in that one class, except the hook_schema?!? Oh, it gets better. There is no hook_schema either, because baseFieldDefinitions automatically defines and sets the schema for your entity's base table. The only catch is that you need to either define the fields before you activate the module or write an update hook for it if you add or change the field afterwards.</p> <p>Let's look at a quick example of how such an entity file might look like:</p> <pre> <code class="language-php">/** * @file * Contains \Drupal\hello\Entity\HelloEntity. */ namespace Drupal\hello\Entity; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\hello\HelloEntityInterface; /** * Defines the Hello entity. * * @ingroup hello * * @ContentEntityType( * id = "hello_world", * label = @Translation("Hello World"), * base_table = "hello_world", * handlers = { * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder", * }, * admin_permission = "administer HelloEntity entity", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid" * }, * links = { * "canonical" = "/admin/hello/{hello_world}", * "edit-form" = "/admin/hello/{hello_world}/edit", * "delete-form" = "/admin/hello/{hello_world}/delete" * }, * field_ui_base_route = "hello.settings" * ) */ class HelloEntity extends ContentEntityBase implements HelloEntityInterface { public function getLabel() { return $this-&gt;get('label')-&gt;value; } public function getValue() { return $this-&gt;get('value')-&gt;value; } /** * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['id'] = BaseFieldDefinition::create('integer') -&gt;setLabel(t('ID')) -&gt;setDescription(t('The ID of the Hello entity.')) -&gt;setReadOnly(TRUE); $fields['uuid'] = BaseFieldDefinition::create('uuid') -&gt;setLabel(t('UUID')) -&gt;setDescription(t('The UUID of the Hello entity.')) -&gt;setReadOnly(TRUE); $fields['entity_id'] = BaseFieldDefinition::create('entity_reference') -&gt;setLabel(t('Node ID')) -&gt;setDescription(t('The node related to this hello')) -&gt;setSettings(array( 'target_type' =&gt; 'node', 'handler' =&gt; 'default', )) -&gt;setDisplayOptions('form', array( 'type' =&gt; 'entity_reference_autocomplete', 'settings' =&gt; array( 'match_operator' =&gt; 'CONTAINS', 'size' =&gt; 60, 'placeholder' =&gt; '', ), 'weight' =&gt; 0, )) -&gt;setRequired(TRUE); $fields['label'] = BaseFieldDefinition::create('string') -&gt;setLabel(t('Label')) -&gt;setDescription(t('The Label of the hello entity')) -&gt;setSetting('max_length', 50) -&gt;setDisplayOptions('form', array( 'type' =&gt; 'textfield', 'settings' =&gt; array( 'size' =&gt; 60, ), 'weight' =&gt; 5, )) -&gt;setRequired(TRUE); $fields['value'] = BaseFieldDefinition::create('string') -&gt;setLabel(t('Value')) -&gt;setDescription(t('Value of the hello entity')) -&gt;setSetting('max_length', 255) -&gt;setDisplayOptions('form', array( 'type' =&gt; 'textfield', 'settings' =&gt; array( 'size' =&gt; 60, ), 'weight' =&gt; 5, )) -&gt;setRequired(TRUE); return $fields; } }</code></pre> <p>As you can see, this one file contains everything to declare and define the entity. As soon as the class is (automatically) loaded by Drupal, the Entity is added to the list, a base table gets established in the database and you may now query it with EntityQuery or load it from the database. It doesn't get any simpler than that.</p> <h3>Drupal 8 entities in practice</h3> <p>Obviously, this isn't enough for any serious work. We need to create add / edit / delete forms, action links, view pages and more. That's a lot of files and a lot of work from scratch, although the power you get by this setup is immense - you can literally create just about anything. But for a simple entity the only purpose of which is to contain and possibly pass on some simple data, it seems like a direct database query would be faster. Right? Not necessarily.</p> <p>Remember, this file structure has a purpose. For instance, back in Drupal 7, every path and their mother was an array in hook_menu. Finding a local task was a royal pain in the ass - in Drupal 8, every task, action, path has a specific place to be at, easy to find, easy to debug, clean to read. So let me give you a hint here: learn the Drupal 8 structure, so you'll know what you're doing, but after you do so, take a peek at <a href="http://drupalconsole.com/">Drupal Console</a>. You don't need it, but you definitely want it. With it, you'll be able to create a module with an entity in a matter of seconds. After that, the imagination is yours.</p> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-inline"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/tags/drupal" hreflang="en">drupal</a></div> </div> </div> <div class="field field--name-dynamic-token-fieldnode-comments-title field--type-ds field--label-hidden field__item"><span>Comments</span> </div> <section class="field field--name-comment field--type-comment field--label-hidden comment-wrapper"> <h2 class="title comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=9&amp;2=comment&amp;3=comment" token="1g0_Z49MsJ1d2YR9jPRW7ku1mahA70BrmRzz7aT_CCI"></drupal-render-placeholder> </section> </div> Wed, 25 Nov 2015 20:51:42 +0000 admin 9 at http://andrejgaluf.com Undefined index: field in field_widget_field() http://andrejgaluf.com/blog/2015-11-25/undefined-index-field-field-widget-field <div data-history-node-id="7" class="node node--type-blog-post node--view-mode-rss ds-1col clearfix"> <div class="field field--name-dynamic-token-fieldnode-custom-submitted field--type-ds field--label-hidden field__item"><p>Submitted by <a href="http://andrejgaluf.com/users/admin">Andrej Galuf</a> on 07. November 2015.</p> </div> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>A few days ago, my colleague was building a custom form in Drupal 7. Everything was going great until he added a file field, at which point all hell broke loose. The data didn't submit and he received these notices:</p> <blockquote> <p>Notice: Undefined index: field in field_widget_field() (Row 578 of ...)<br /> Notice: Undefined index: instance in field_widget_instance() (Row 603 of ....)</p> </blockquote> <p>Since the field was using a custom widget, we were first debugging there, until we finally found the real culprit somewhere completely else: passing by reference.</p> <p>The hook_form in Drupal 7 has two parameters: $form and $form_state. What many developers forget and mostly isn't that big a deal, but proved to be critical in this case is passing the $form_state by reference. If you are seeing the above notices, first make sure you haven't forgotten the ampersand (&amp;) before blaming anything else.</p> <p>For reference, this is the correct code:</p> <pre> <code class="language-php">/** * Implements hook_form(). */ function MYMODULE_form($form, &amp;$form_state) { // Your code here }</code></pre> <p> </p> </div> <div class="field field--name-field-tags field--type-entity-reference field--label-inline"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/tags/drupal" hreflang="en">drupal</a></div> <div class="field__item"><a href="/tags/quick-tips" hreflang="en">quick tips</a></div> </div> </div> <div class="field field--name-dynamic-token-fieldnode-comments-title field--type-ds field--label-hidden field__item"><span>Comments</span> </div> <section class="field field--name-comment field--type-comment field--label-hidden comment-wrapper"> <h2 class="title comment-form__title">Add new comment</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=7&amp;2=comment&amp;3=comment" token="XclDo4EBJ-co3K9OihriJH27UPGfd4KD5wbKh9JYH34"></drupal-render-placeholder> </section> </div> Sat, 07 Nov 2015 09:58:23 +0000 admin 7 at http://andrejgaluf.com