theoleschool - data module http://theoleschool.com/tags/data-module en Using the Data Module's "Relate to Node" Functionality http://theoleschool.com/blog/using-data-modules-relate-node-functionality <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>While I'm <a href="/blog/adopting-table-schema-using-data-module">on the topic</a> of the <a href="http://drupal.org/project/data">Data module</a>, I'd like to share my thoughts on some of the confusion surrounding use of the Data Node module included with the package. (see issues <a href="http://drupal.org/node/976292">976292</a> and <a href="http://drupal.org/node/898562">898562</a>)</p> <p>It's my opinion that the bulk of the functionality in the Data Node module has been intentionally "left undone." Instead, the module is giving us the tools to allow us to implement node relationships as needed, ourselves (think API). My thinking is that this was done to allow the maximum amount of flexibility in relating other data to nodes by minimizing assumptions about what the relationship should be. <em>I have in no way validated this with any of the module maintainers, nor is it denoted in any documentation that I am aware of.</em> What follows is my "average" use case for this module, and some thoughts on more advanced implementations.</p> <img src="/sites/default/files/u3/relate-to-node-form.png" style="float:right; margin: 0 0 0 10px; width:250px;" /><p>When enabled, this functionality is apparent in Drupal's admin UI at <span class="geshifilter"><code class="php geshifilter-php">admin<span style="color: #339933;">/</span>build<span style="color: #339933;">/</span>data<span style="color: #339933;">/</span>edit<span style="color: #339933;">/%</span>table_name<span style="color: #339933;">/</span>node</code></span> by clicking on the edit link next to a table at <span class="geshifilter"><code class="php geshifilter-php">admin<span style="color: #339933;">/</span>build<span style="color: #339933;">/</span>data<span style="color: #339933;">/</span>overview</code></span> then clicking the "Relate to Nodes" tab. This page presents a form asking you which content type you would like the table related to, and the column name of the id key in the table you are referencing. Simple right? Well sorta... When you submit this form and happily skip over to views to reference the node table, it quickly becomes apparent that nothing has happened. Checking the <span class="geshifilter"><code class="php geshifilter-php">data_table_node</code></span> table in the database will reveal it to be empty. So what's up here? Is this just an unfinished piece?</p> <p>After taking a look in the data_node.module things start to make a bit more sense. At first glance we see some tantalizing functions, especially:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/**<br />  * Add a relationship between a data table and a node.<br />  */</span><br /><span style="color: #000000; font-weight: bold;">function</span> data_node_add<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <span style="color: #0000ff;">$save</span> = <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><br />     <span style="color: #ff0000;">'data_table_name'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #0000ff;">$table</span>-<span style="color: #66cc66;">&gt;</span><span style="color: #006600;">get</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">)</span>,<br />     <span style="color: #ff0000;">'id'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #0000ff;">$id</span>,<br />     <span style="color: #ff0000;">'nid'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #0000ff;">$nid</span>,<br />   <span style="color: #66cc66;">)</span>;<br />   <span style="color: #b1b100;">return</span> <a href="http://api.drupal.org/api/function/drupal_write_record/6"><span style="color: #000066;">drupal_write_record</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'data_table_node'</span>, <span style="color: #0000ff;">$save</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #66cc66;">}</span><br /><span style="color: #808080; font-style: italic;">/**<br />  * Remove a relationship between a data table and a node.<br />  */</span><br /><span style="color: #000000; font-weight: bold;">function</span> data_node_remove<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <a href="http://api.drupal.org/api/function/db_query/6"><span style="color: #000066;">db_query</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">"DELETE FROM {data_table_node} WHERE data_table_name = '%s' AND id = %d AND nid = %d"</span>, <span style="color: #0000ff;">$table</span>-<span style="color: #66cc66;">&gt;</span><span style="color: #006600;">get</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">)</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #66cc66;">}</span></div></div> <p>Typically I'll stop right here. I really only use the Data module for custom reporting, and when doing so I handle the CRUD functions for the new data type myself. My data type's row reference to nodes are nearly always a 1 to 1 mapping. To further simplify things for myself, the <span class="geshifilter"><code class="php geshifilter-php"><span style="color: #000088;">$id</span></code></span> I use in my custom data type is often the node id of it's intended reference. This makes my calls to these functions look something like:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;">  <span style="color: #808080; font-style: italic;">//insert my_table with a nid key</span><br />   ...<br />   <span style="color: #808080; font-style: italic;">//inform Data Node of the reference</span><br />   <span style="color: #0000ff;">$my_table_object</span> = data_get_table<span style="color: #66cc66;">(</span><span style="color: #ff0000;">'my_table'</span><span style="color: #66cc66;">)</span>;<br />   data_node_add<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$my_table_object</span>, <span style="color: #0000ff;">$nid</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>;</div></div> <p>This will populate the <span class="geshifilter"><code class="php geshifilter-php">data_table_node</code></span> table so that when we use the relationship in out data type view it will return results. Of course deletion will be handled similarly.</p> <p>This is admittedly a somewhat basal interaction with this module, though it is often enough to satisfy my needs. However Data Node allows for much more complexity and has a certain level of user interaction built in just below the surface.</p> <p>It's not until we start thinking about the data structures involved that the scope of the allowed complexity becomes apparent. It is easy to assume that referencing a data type to a node is always going to be 1 to 1 (much like the typical use of CCK's Node Reference.) However, this may not always be the case. It is not required that the data table's id be the nid of the node it is referencing. It only has to be unique within the table. Because of this, a large field of possibilities emerge. You may have data type rows referencing many nodes each (1 to many), many data type rows referencing a single node (many to 1), or, of course, any number of data type rows referencing any number of nodes (many to many). The <span class="geshifilter"><code class="php geshifilter-php">data_table_node</code></span> table along with the provided CRUD functions, like the ones mentioned above, will allow for this. I think that this flexibility is a big reason that the module has been left so open.</p> <p>Lastly, a closer look at the Data Node module will reveal some rather fancy display functions for the underlying relationship CRUD.</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/**<br />  * Render an add link for a given item..<br />  */</span><br /><span style="color: #000000; font-weight: bold;">function</span> data_node_render_add_link<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <a href="http://api.drupal.org/api/function/drupal_add_css/6"><span style="color: #000066;">drupal_add_css</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/drupal_get_path/6"><span style="color: #000066;">drupal_get_path</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'module'</span>, <span style="color: #ff0000;">'data_node'</span><span style="color: #66cc66;">)</span> . <span style="color: #ff0000;">'/data_node.css'</span><span style="color: #66cc66;">)</span>;<br />   <a href="http://api.drupal.org/api/function/drupal_add_js/6"><span style="color: #000066;">drupal_add_js</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/drupal_get_path/6"><span style="color: #000066;">drupal_get_path</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'module'</span>, <span style="color: #ff0000;">'data_node'</span><span style="color: #66cc66;">)</span> . <span style="color: #ff0000;">'/data_node.js'</span><span style="color: #66cc66;">)</span>;<br /><br />   <span style="color: #0000ff;">$title</span> = _data_node_get_title<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #0000ff;">$table_name</span> = <span style="color: #0000ff;">$table</span>-<span style="color: #66cc66;">&gt;</span><span style="color: #006600;">get</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #0000ff;">$class</span> = <span style="color: #ff0000;">"data_node_link-{$table_name}-{$id}-{$nid}"</span>;<br />   <span style="color: #b1b100;">return</span> <a href="http://api.drupal.org/api/function/l/6"><span style="color: #000066;">l</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/t/6"><span style="color: #000066;">t</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'Add to !title'</span>, <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'!title'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #0000ff;">$title</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>, data_node_add_path<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>, <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'attributes'</span> =<span style="color: #66cc66;">&gt;</span> <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'class'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #ff0000;">"data-node-add $class"</span><span style="color: #66cc66;">)</span>, <span style="color: #ff0000;">'query'</span> =<span style="color: #66cc66;">&gt;</span> <a href="http://api.drupal.org/api/function/drupal_get_destination/6"><span style="color: #000066;">drupal_get_destination</span></a><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #66cc66;">}</span><br /><br /><span style="color: #808080; font-style: italic;">/**<br />  * Render a remove link for a given item.<br />  */</span><br /><span style="color: #000000; font-weight: bold;">function</span> data_node_render_remove_link<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <a href="http://api.drupal.org/api/function/drupal_add_css/6"><span style="color: #000066;">drupal_add_css</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/drupal_get_path/6"><span style="color: #000066;">drupal_get_path</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'module'</span>, <span style="color: #ff0000;">'data_node'</span><span style="color: #66cc66;">)</span> . <span style="color: #ff0000;">'/data_node.css'</span><span style="color: #66cc66;">)</span>;<br />   <a href="http://api.drupal.org/api/function/drupal_add_js/6"><span style="color: #000066;">drupal_add_js</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/drupal_get_path/6"><span style="color: #000066;">drupal_get_path</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'module'</span>, <span style="color: #ff0000;">'data_node'</span><span style="color: #66cc66;">)</span> . <span style="color: #ff0000;">'/data_node.js'</span><span style="color: #66cc66;">)</span>;<br /><br />   <span style="color: #0000ff;">$title</span> = _data_node_get_title<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #0000ff;">$table_name</span> = <span style="color: #0000ff;">$table</span>-<span style="color: #66cc66;">&gt;</span><span style="color: #006600;">get</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #0000ff;">$class</span> = <span style="color: #ff0000;">"data_node_link-{$table_name}-{$id}-{$nid}"</span>;<br />   <span style="color: #b1b100;">return</span> <a href="http://api.drupal.org/api/function/l/6"><span style="color: #000066;">l</span></a><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/t/6"><span style="color: #000066;">t</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'Remove from !title'</span>, <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'!title'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #0000ff;">$title</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>, data_node_remove_path<span style="color: #66cc66;">(</span><span style="color: #0000ff;">$table</span>, <span style="color: #0000ff;">$id</span>, <span style="color: #0000ff;">$nid</span><span style="color: #66cc66;">)</span>, <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'attributes'</span> =<span style="color: #66cc66;">&gt;</span> <a href="http://www.php.net/array"><span style="color: #000066;">array</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'class'</span> =<span style="color: #66cc66;">&gt;</span> <span style="color: #ff0000;">"data-node-remove $class"</span><span style="color: #66cc66;">)</span>, <span style="color: #ff0000;">'query'</span> =<span style="color: #66cc66;">&gt;</span> <a href="http://api.drupal.org/api/function/drupal_get_destination/6"><span style="color: #000066;">drupal_get_destination</span></a><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #66cc66;">}</span></div></div> <p>These are the two pertinent functions. They both return a link that will hit a page callback to do just what you would expect. Add or remove a node relationship. This allows you "expose" the functionality to the user. The link could be placed on a view or node page to allow users to add or remove the relation to a specific data type themselves, "on the fly". This, in combination with the flexibility of the relationship itself, lends to a huge variety of possible use cases with relatively few function calls.</p> <p>So wrapping this up. Data Node module is best thought of as an API. It doesn't do so much right out of the box, but with minimal implementation it becomes a fairly powerful tool.</p></div></div></div> Sun, 04 Mar 2012 03:44:18 +0000 rho 157 at http://theoleschool.com Adopting a Table with Schema using Data Module http://theoleschool.com/blog/adopting-table-schema-using-data-module <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><p>When working with medium to larger sized web applications, inevitably the requirement comes up for some advanced data reporting. The spectrum of requests here can obviously be pretty broad and is often beyond what you can accomplish with the default tables provided by Drupal and the installed modules. This tends to lead us to creating custom tables to track and manage the requested reports. Along with writing all of the necessary <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> functions to make sure our new table is managing data appropriately, we also have to provide some sort of display or an export. Views can handle this latter requirement quite nicely. So it is here that we can turn to modules like <a href="http://drupal.org/project/data">Data</a> or the now depricated <a href="http://drupal.org/project/tw">Table Wizard</a> to bring our new table under views "control." This gives us the added bonus of taking the display configuration out of the hands of the developer and placing it into the site builder/administrator's domain.</p> <p>In a recent project just such a reporting request came up. The report was fairly complex and was to be visible to a larger subset of users. I wound up managing this report with a custom table and I turned to Data module to allow views to handle the output. After installing my custom module which created the custom table, I navigated to <span class="geshifilter"><code class="php geshifilter-php">admin<span style="color: #339933;">/</span>build<span style="color: #339933;">/</span>data<span style="color: #339933;">/</span>adopt</code></span>. Expecting to see my table in the list of "adoptable" tables, I was a bit confused when it was nowhere to be found. After <a href="http://drupal.org/node/888946#comment-3360326">a bit of digging</a> I realized that tables declared using <a href="http://drupal.org/node/146843">Schema API</a> were considered "owned" by the module and not included in the list of adoptable tables. This makes some sense as you don't want just any table adopted by Data. Just imagine when an admin who's not quite sure what he or she is doing, decides to adopt and then drop the node table. I'd rather not...</p> <p>So this leaves us with the question, <em>"How do I get Data module to adopt my table?"</em> From what I can tell, the creators of the data module expect us to use the Data API to handle table creation and CRUD. Personally I don't like the idea of this. I already understand and like working with Drupal's core Schema API, and don't want to adopt another layer of abstraction. I only want to utilize Data module to throw my table up to views. So after looking through how adoption is handled in the module, I found that really there is nothing preventing Data from adopting these schema declared tables. They are simply being made unavailable to the form at <span class="geshifilter"><code class="php geshifilter-php">admin<span style="color: #339933;">/</span>build<span style="color: #339933;">/</span>data<span style="color: #339933;">/</span>adopt</code></span>. This lead me to attempt to adopt my table right after it is generated in my module's .install file. This wound up working quite nicely and is implemented as follows:</p> <div class="geshifilter"><div class="drupal6 geshifilter-drupal6" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> my_module_install<span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span> <span style="color: #66cc66;">{</span><br />   <span style="color: #808080; font-style: italic;">//install schema as normal</span><br />   <a href="http://api.drupal.org/api/function/drupal_install_schema/6"><span style="color: #000066;">drupal_install_schema</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'my_table'</span><span style="color: #66cc66;">)</span>;<br />   <br />   <span style="color: #808080; font-style: italic;">//make sure we have the data module</span><br />   <span style="color: #b1b100;">include_once</span><span style="color: #66cc66;">(</span><a href="http://api.drupal.org/api/function/drupal_get_path/6"><span style="color: #000066;">drupal_get_path</span></a><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'module'</span>, <span style="color: #ff0000;">'data'</span><span style="color: #66cc66;">)</span> .<span style="color: #ff0000;">'/data.module'</span><span style="color: #66cc66;">)</span>;<br />   data_include<span style="color: #66cc66;">(</span><span style="color: #ff0000;">'DataTable'</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #808080; font-style: italic;">//instanciate the table we just created with the DataTable class</span><br />   <span style="color: #0000ff;">$my_table_object</span> = DataTable::<span style="color: #006600;">instance</span><span style="color: #66cc66;">(</span><span style="color: #ff0000;">'my_table'</span><span style="color: #66cc66;">)</span>;<br />   <span style="color: #808080; font-style: italic;">//tell Data module to adopt the table</span><br />   <span style="color: #0000ff;">$my_table_object</span>-<span style="color: #66cc66;">&gt;</span><span style="color: #006600;">adopt</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;<br />   DataTable::<span style="color: #006600;">clearCaches</span><span style="color: #66cc66;">(</span><span style="color: #66cc66;">)</span>;<br /><span style="color: #66cc66;">}</span></div></div> <p>After installing the module you should be able to see your custom table at <span class="geshifilter"><code class="php geshifilter-php">admin<span style="color: #339933;">/</span>build<span style="color: #339933;">/</span>data</code></span>. Data module is now obviously a dependency so be sure that it is installed and enabled before installing your custom module.</p> <p>I realize that I could write my own views handlers for the custom table data I am providing, but I have found that to be a fairly time consuming (and often esoteric) task. When all we are after is a single, somewhat unique report, I feel that integration with the Data module and it's corresponding integration with views is simple, quick, and more than adequate.</p></div></div></div> Sat, 03 Mar 2012 20:19:36 +0000 rho 156 at http://theoleschool.com