{"id":153,"date":"2010-04-14T13:20:46","date_gmt":"2010-04-14T13:20:46","guid":{"rendered":"http:\/\/nicholshayes.myzen.co.uk\/blog\/?p=153"},"modified":"2010-05-18T14:45:17","modified_gmt":"2010-05-18T14:45:17","slug":"self-referencing-objects","status":"publish","type":"post","link":"http:\/\/nicholshayes.co.uk\/blog\/?p=153","title":{"rendered":"Self referencing objects"},"content":{"rendered":"<p>My application has products, and each product can have components which are themselves products. I achieve this via a pair of self-referencing has_and_belongs_to_many relationships.<\/p>\n<div class=\"codecolorer-container ruby default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"ruby codecolorer\">&nbsp; has_and_belongs_to_many <span class=\"re3\">:components<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:class_name<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">'Product'<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:join_table<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;product_grouping&quot;<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:association_foreign_key<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;component_product_id&quot;<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:foreign_key<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;group_product_id&quot;<\/span><br \/>\n<br \/>\n&nbsp; has_and_belongs_to_many <span class=\"re3\">:groups<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:class_name<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">'Product'<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:join_table<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;product_grouping&quot;<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:association_foreign_key<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;group_product_id&quot;<\/span>,<br \/>\n&nbsp; &nbsp; <span class=\"re3\">:foreign_key<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"st0\">&quot;component_product_id&quot;<\/span><\/div><\/div>\n<p>Note the presence of the join table product_grouping which has two fields: group_product_id and component_product_id<\/p>\n<p>I wanted to be able to collect together all components within a product hierarchy. So if my products are car, engine and piston: piston is a component of engine, which is a component of car. However, car.components would only return engine. I needed a method that would traverse the whole component hierarchy.<\/p>\n<p>I achieved this by using a method that referenced itself within a collect:<\/p>\n<div class=\"codecolorer-container ruby default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"ruby codecolorer\">&nbsp; <span class=\"kw1\">def<\/span> all_components<br \/>\n&nbsp; &nbsp; components.<span class=\"me1\">collect<\/span><span class=\"br0\">&#123;<\/span><span class=\"sy0\">|<\/span>c<span class=\"sy0\">|<\/span> <span class=\"br0\">&#91;<\/span>c<span class=\"br0\">&#93;<\/span> <span class=\"sy0\">&lt;&lt;<\/span> c.<span class=\"me1\">all_components<\/span><span class=\"br0\">&#125;<\/span>.<span class=\"me1\">flatten<\/span>.<span class=\"me1\">uniq<\/span><br \/>\n&nbsp; <span class=\"kw1\">end<\/span><\/div><\/div>\n<p>I&#8217;ve seen this technique used before, but always had a little difficulty getting my head around it. I found that the key to getting it working was to make sure that the method always returned an array or nil.<\/p>\n<p>Now:<\/p>\n<div class=\"codecolorer-container ruby default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"ruby codecolorer\">&nbsp; car.<span class=\"me1\">components<\/span> <span class=\"sy0\">-&gt;<\/span> <span class=\"br0\">&#91;<\/span>engine<span class=\"br0\">&#93;<\/span><br \/>\n&nbsp; car.<span class=\"me1\">all_components<\/span> <span class=\"sy0\">-&gt;<\/span> <span class=\"br0\">&#91;<\/span>engine, piston<span class=\"br0\">&#93;<\/span><\/div><\/div>\n<p>What effectively is happening with car.all_components is this:<\/p>\n<ul>\n<li> we first call all_components on car.<\/li>\n<li> it works through each component and returns that component plus what ever is returned by its all_components. So as there is only one component (engine) engine.all_components is called, its results are returned in an array with a copy of itself.<\/li>\n<li>engine.all_components acts on its only component piston. So it returns piston and the results of piston.all_components<\/li>\n<li>piston has no components, so piston.all_components returns nil and the process ends.<\/li>\n<li>flatten then gets rid of any array within array issues.<\/li>\n<li>uniq tidies up instances where two products may share the same component.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>My application has products, and each product can have components which are themselves products. I achieve this via a pair of self-referencing has_and_belongs_to_many relationships. &nbsp; has_and_belongs_to_many :components, &nbsp; &nbsp; :class_name =&gt; &#8216;Product&#8217;, &nbsp; &nbsp; :join_table =&gt; &quot;product_grouping&quot;, &nbsp; &nbsp; :association_foreign_key &hellip; <a href=\"http:\/\/nicholshayes.co.uk\/blog\/?p=153\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[],"_links":{"self":[{"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/153"}],"collection":[{"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=153"}],"version-history":[{"count":16,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/153\/revisions"}],"predecessor-version":[{"id":210,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/153\/revisions\/210"}],"wp:attachment":[{"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=153"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/nicholshayes.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}