<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
> <channel><title>Project 2061 Techlog &#187; CakePHP</title> <atom:link href="http://techlog.p2061.org/category/cakephp/feed/" rel="self" type="application/rss+xml" /><link>http://techlog.p2061.org</link> <description>Blogging from Project 2061 technology group</description> <lastBuildDate>Tue, 06 Sep 2011 14:49:39 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Optimizing database queries in CakePHP</title><link>http://techlog.p2061.org/2011/08/19/optimizing-database-queries-in-cakephp/</link> <comments>http://techlog.p2061.org/2011/08/19/optimizing-database-queries-in-cakephp/#comments</comments> <pubDate>Fri, 19 Aug 2011 17:01:09 +0000</pubDate> <dc:creator>bsweeney</dc:creator> <category><![CDATA[CakePHP]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Web Development]]></category> <category><![CDATA[CakePHP 1.2.x]]></category> <category><![CDATA[CakepHP 1.3.x]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[optimization]]></category> <category><![CDATA[sql]]></category> <guid
isPermaLink="false">http://techlog.p2061.org/?p=182</guid> <description><![CDATA[We&#8217;ve been using CakePHP for a while now. I&#8217;ve found it to be very helpful for quick prototyping and development of core site functionality. This is nice because it allows more time for tweaking a site&#8217;s user interface, functionality, and performance. That last one, performance, can occasionally be a challenge. One of the means of [...]]]></description> <content:encoded><![CDATA[<p>We&#8217;ve been using CakePHP for a while now. I&#8217;ve found it to be very helpful for quick prototyping and development of core site functionality. This is nice because it allows more time for tweaking a site&#8217;s user interface, functionality, and performance. That last one, performance, can occasionally be a challenge. One of the means of optimizing a site&#8217;s performance is through the crafting of database queries that take into account indexes and conditions. But because CakePHP is building the SQL we have to keep in mind how CakePHP does this as we construct our query parameters.</p><p>Let&#8217;s review one of those situations. Say we have the following (simplified) models:</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> Item <span style="color: #000000; font-weight: bold;">extends</span> AppModel <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$hasMany</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$belongsTo</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Packet'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> Student <span style="color: #000000; font-weight: bold;">extends</span> AppModel <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$hasMany</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> Answer <span style="color: #000000; font-weight: bold;">extends</span> AppModel <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$belongsTo</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Student'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'Item'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'Packet'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> Packet <span style="color: #000000; font-weight: bold;">extends</span> AppModel <span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$hasMany</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'Student'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$hasAndBelongsToMany</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div><p>Let me explain the model relationships. We have test items that are assigned to packets. Packets are sent out for testing to a group of student. So each answer is unique based on the packet, item, and student. The (somewhat circular) relationships between these models provides for easy access to specific data. For example, let&#8217;s say we want to know all the answers from a particular state for a couple of our items. Here&#8217;s an example query that present some optimization issues:</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$queryOpts</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
  <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'contain'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'Answer'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'Answer.packet_id IN (SELECT id FROM packets WHERE state = &quot;CA&quot;)'</span>
      <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$items</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Item</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'all'</span><span style="color: #339933;">,</span><span style="color: #000088;">$queryOpts</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div><p>You&#8217;ll note that I&#8217;m not using CakePHP syntax for the Answer model conditional. We had been using this conditional in our web application prior to moving to CakePHP. Since there is not, so far as I can tell, an easy way to work with SQL subqueries we decided to start by forcing a query similar to the original.</p><p>Running the above creates the following SQL statement when querying the answers table:</p><div
class="wp_syntax"><div
class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.*</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`answers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Answer`</span> <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`packet_id`</span> <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> id <span style="color: #993333; font-weight: bold;">FROM</span> packets <span style="color: #993333; font-weight: bold;">WHERE</span> packet_type <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;F&quot;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`item_id`</span> <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #66cc66;">&#41;</span></pre></div></div><p>We have a fairly large data set so the above query takes ~32 seconds to run on my system. But that query is not optimized; reordering the conditions in the WHERE clause produces the following query:</p><div
class="wp_syntax"><div
class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.*</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`answers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Answer`</span> <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`item_id`</span> <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #66cc66;">&#41;</span> <span style="color: #993333; font-weight: bold;">AND</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`packet_id`</span> <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #66cc66;">&#40;</span><span style="color: #993333; font-weight: bold;">SELECT</span> id <span style="color: #993333; font-weight: bold;">FROM</span> packets <span style="color: #993333; font-weight: bold;">WHERE</span> packet_type <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">&quot;F&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div><p>The optimized query performs significantly better, &lt;1 second.</p><p>Since for this example we know which items we want we can update the query to manually force the desired ordering:</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$queryOpts</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
  <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'contain'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'Answer'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'Item.id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #009900;">&#41;</span>
        <span style="color: #0000ff;">'Answer.packet_id IN (SELECT id FROM packets WHERE state = &quot;CA&quot;)'</span>
      <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$items</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Item</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'all'</span><span style="color: #339933;">,</span><span style="color: #000088;">$queryOpts</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div><p>But what if we don&#8217;t know what items we want. Say we&#8217;re pulling items (and answers) as part of another containable query. We have no way to specify the items in the Answer model conditionals. But we can improve the query so that <a
title="gauravgs asked How do I write a subquery in cakephp at CakePHP Questions" href="http://ask.cakephp.org/questions/view/how_do_i_write_a_subquery_in_cakephp">we&#8217;re not using a subquery</a> each time (i.e. we&#8217;re doing it the &#8220;CakePHP&#8221; way):</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$fields</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Packet.id'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$conditions</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Packet.packet_type'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'F'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$packet_list</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Item</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Packet</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'list'</span><span style="color: #339933;">,</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'fields'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$fields</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'conditions'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$conditions</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$queryOpts</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
  <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Item.id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1995</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1726</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1707</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #0000ff;">'contain'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'Answer'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'fields'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Answer.*'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #0000ff;">'conditions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'Answer.packet_id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$packet_list</span>
      <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$items</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Item</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'all'</span><span style="color: #339933;">,</span><span style="color: #000088;">$queryOpts</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div><p>Which produces the following two queries:</p><div
class="wp_syntax"><div
class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`Packet`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`id`</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`packets`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Packet`</span> <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #ff0000;">`Packet`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`packet_type`</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">'F'</span>;
<span style="color: #993333; font-weight: bold;">SELECT</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.*,</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`item_id`</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #ff0000;">`answers`</span> <span style="color: #993333; font-weight: bold;">AS</span> <span style="color: #ff0000;">`Answer`</span> <span style="color: #993333; font-weight: bold;">WHERE</span> <span style="color: #ff0000;">`Answer`</span><span style="color: #66cc66;">.</span><span style="color: #ff0000;">`packet_id`</span> <span style="color: #993333; font-weight: bold;">IN</span> <span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">1278</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1277</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1276</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1274</span><span style="color: #66cc66;">,...,</span><span style="color: #cc66cc;">1726</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1971</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1972</span><span style="color: #66cc66;">,</span> <span style="color: #cc66cc;">1995</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div><p>Again we&#8217;re seeing the kind of performance we want, &lt;1 second for both queries. As good as when we knew all our conditions in advance. The main drawback to doing it this way is the size of the query string sent to your database. If your first query is pulling a large number of results then your second query will be quite large. And most databases have limits on how large a query can be.</p><p>This isn&#8217;t the only way to get to the end point either. As with any programming task there are a variety of ways to arrive at the desired result. For example, you could break up the query into multiple queries that build off of the previous query. Sticking with our items/answers query you could first get the items in one query. Then <code>foreach</code> through the results and run a query to get the answers, integrating the results manually. It&#8217;s more work, and likely would decrease performance since you&#8217;re increasing the number of database queries, but if you can&#8217;t seem to optimize your all-in-one queries it&#8217;s worthwhile to try other options to see if you can get better performance.</p><p>Aside: performance problems due to poorly optimized queries can be somewhat mitigated by query caching. Even so, you&#8217;re better of with an optimized query to begin with since it saves you from a performance hit every time the relevant cached result is flushed.</p> ]]></content:encoded> <wfw:commentRss>http://techlog.p2061.org/2011/08/19/optimizing-database-queries-in-cakephp/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>CakePHP and CURRENT_TIMESTAMP</title><link>http://techlog.p2061.org/2009/04/17/cakephp-and-current_timestamp/</link> <comments>http://techlog.p2061.org/2009/04/17/cakephp-and-current_timestamp/#comments</comments> <pubDate>Fri, 17 Apr 2009 20:15:54 +0000</pubDate> <dc:creator>bsweeney</dc:creator> <category><![CDATA[CakePHP]]></category> <category><![CDATA[MySQL]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Web Development]]></category> <guid
isPermaLink="false">http://techlog.p2061.org/?p=229</guid> <description><![CDATA[As of cakePHP 1.2.1.8004 you can&#8217;t use CURRENT_TIMESTAMP as the default for a column. With MySQL when the default is set to CURRENT_TIMESTAMP the current date+time is inserted for a new row that doesn&#8217;t specifically define the value of the column. From what I can tell, when CakePHP specifies the values for all columns when [...]]]></description> <content:encoded><![CDATA[<p>As of cakePHP 1.2.1.8004 you can&#8217;t use CURRENT_TIMESTAMP as the default for a column. With MySQL when the default is set to CURRENT_TIMESTAMP the current date+time is inserted for a new row that doesn&#8217;t specifically define the value of the column.</p><p>From what I can tell, when CakePHP specifies the values for all columns when it creates a new record. A column with a defined default that is not specifically set by the user is manually set by CakePHP (rather than let MySQL handle defaults upon record insertion). But CakePHP doesn&#8217;t understand the CURRENT_TIMESTAMP keyword and so treats it as a string and wraps it in quotes. This breaks the resulting INSERT statement and you receive an error:</p><blockquote><p>Incorrect datetime value: &#8216;CURRENT_TIMESTAMP&#8217;</p></blockquote><p>Interestingly, columns that are named &#8220;created&#8221; and &#8220;modified&#8221; receive special handling by CakePHP. These columns are treated like auto-update columns by CakePHP and it sets them as expected. With the special handling of these columns in mind it is possible to get around the CURRENT_TIMESTAMP bug by following the recommended settings for created/modified columns, i.e. specifying the field as DATETIME with a default of NULL. CakePHP will automatically update the columns when inserting/updating records.</p><p>References:</p><ul><li><a
href="http://dev.mysql.com/doc/refman/5.0/en/timestamp.html">MySQL Reference Manual: 10.3.1.1. TIMESTAMP Properties</a></li><li><a
href="http://book.cakephp.org/view/69/created-and-modified">CakePHP Cookbook: 3.7.2.3 created and modified</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://techlog.p2061.org/2009/04/17/cakephp-and-current_timestamp/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Authentication &amp; Authorization with Scaffolding</title><link>http://techlog.p2061.org/2009/04/17/authentication-authorization-with-scaffolding/</link> <comments>http://techlog.p2061.org/2009/04/17/authentication-authorization-with-scaffolding/#comments</comments> <pubDate>Fri, 17 Apr 2009 18:45:17 +0000</pubDate> <dc:creator>bsweeney</dc:creator> <category><![CDATA[CakePHP]]></category> <category><![CDATA[Web Development]]></category> <guid
isPermaLink="false">http://techlog.p2061.org/?p=220</guid> <description><![CDATA[Though scaffolding is not recommended for production sites, I&#8217;ve found it quite handy when just getting started. Unfortunately, it doesn&#8217;t appear that the authentication/authorization (auth^2) mechanism works with scaffolding. You can, however, get auth*2 working manually with just a few lines of code. First, follow the steps of the Simple Acl controlled Application tutorial from [...]]]></description> <content:encoded><![CDATA[<p>Though scaffolding is not recommended for production sites, I&#8217;ve found it quite handy when just getting started. Unfortunately, it doesn&#8217;t appear that the authentication/authorization (auth^2) mechanism works with scaffolding. You can, however, get auth*2 working manually with just a few lines of code.</p><p>First, follow the steps of the <a
href="http://book.cakephp.org/view/641/Simple-Acl-controlled-Application">Simple Acl controlled Application tutorial</a> from the CakePHP cookbook up to the section on logging in.</p><p>Next, we need to insert the <a
href="http://book.cakephp.org/view/645/Acts-As-a-Requester">code that updates the ARO</a> when a user is added or edited. Normally you would place this code in your add/edit action in the users controller, but for scaffolded actions we&#8217;ll use another callback function. Add the following code to your users controller:</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> _afterScaffoldSave<span style="color: #009900;">&#40;</span><span style="color: #000088;">$action</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #000088;">$aro</span> <span style="color: #339933;">=&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Acl<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Aro<span style="color: #339933;">;</span>
  <span style="color: #000088;">$user</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$aro</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>findByForeignKeyAndModel<span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>data<span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'User'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'id'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'User'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$group</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$aro</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>findByForeignKeyAndModel<span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>data<span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'User'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'group_id'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'Group'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$aro</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>id <span style="color: #339933;">=</span> <span style="color: #000088;">$user</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Aro'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'id'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
  <span style="color: #000088;">$aro</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>save<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'parent_id'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$group</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Aro'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'id'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div><p>Note: for scaffold callbacks you <strong>must</strong> <code>return TRUE;</code> or the scaffold will not finish building the page.</p><p>The above code has been modified from the original in the tutorial, which included a conditional that checked for a change in the user&#8217;s group. As far as I can tell scaffolding causes CakePHP to return only the updated record (even when using the _beforeScaffold method) so you&#8217;re unable to compare the old and new values. As a result you have to update the ARO with every update, even if the user&#8217;s group is not updated.</p><p>Finally, for scaffolded actions we need a way to determine if the user is authorized. The AuthComponent has all the functionality we need. Add the following function to any controller using scaffolding that needs auth*2:</p><div
class="wp_syntax"><div
class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> _beforeScaffold<span style="color: #009900;">&#40;</span><span style="color: #000088;">$action</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>user<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #009900; font-weight: bold;">NULL</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #339933;">!</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'*'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>allowedActions<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #339933;">!</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$action</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>allowedActions<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Session<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>write<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Auth.redirect'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'/'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>name <span style="color: #339933;">.</span> <span style="color: #0000ff;">'/'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$action</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>loginRedirect <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'controller'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>name<span style="color: #339933;">,</span> <span style="color: #0000ff;">'action'</span> <span style="color: #339933;">=&amp;</span>gt<span style="color: #339933;">;</span> <span style="color: #000088;">$action</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>redirect<span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>loginAction<span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'*'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>allowedActions<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #339933;">!</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$action</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>allowedActions<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>user<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!==</span> <span style="color: #009900; font-weight: bold;">NULL</span> <span style="color: #339933;">&amp;</span>amp<span style="color: #339933;">;&amp;</span>amp<span style="color: #339933;">;</span> <span style="color: #339933;">!</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Acl<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>check<span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>user<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>action<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$url</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'/'</span> <span style="color: #339933;">.</span> <span style="color: #990000;">implode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/'</span><span style="color: #339933;">,</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Auth<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>loginAction<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>referer<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> ? <span style="color: #0000ff;">'/'</span> <span style="color: #339933;">:</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>referer<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>Session<span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>setFlash<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'You do not have permission to perform that action.'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&amp;</span>gt<span style="color: #339933;">;</span>redirect<span style="color: #009900;">&#40;</span><span style="color: #000088;">$url</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div><p>This function checks to see if the user is logged in when accessing restricted actions. If not, the user is redirected to the login page. If so, and if the user is attempting to access a page for which he has no permissions, then the user is bounced back to the referring page.</p><p>Of course, you can skip all this if you build a skeleton CRUD using <code>cake bake</code> and specify <em>not</em> to use scaffolding.</p> ]]></content:encoded> <wfw:commentRss>http://techlog.p2061.org/2009/04/17/authentication-authorization-with-scaffolding/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>CakePHP, Apache, and Ampersands</title><link>http://techlog.p2061.org/2009/01/15/cakephp-apache-and-ampersands/</link> <comments>http://techlog.p2061.org/2009/01/15/cakephp-apache-and-ampersands/#comments</comments> <pubDate>Thu, 15 Jan 2009 19:44:23 +0000</pubDate> <dc:creator>bsweeney</dc:creator> <category><![CDATA[CakePHP]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[Web Development]]></category> <guid
isPermaLink="false">http://techlog.p2061.org/?p=169</guid> <description><![CDATA[I&#8217;m still learning CakePHP. My most recent experimentation is with forms and URL redirecting. In the process I noticed that if the URL includes an ampersand, even in URL-encoded form, CakePHP will read in the URL incorrectly. In my case, I was taking a form submission and redirecting it to a new page, appending the [...]]]></description> <content:encoded><![CDATA[<p>I&#8217;m still learning CakePHP. My most recent experimentation is with forms and URL redirecting. In the process I noticed that if the URL includes an ampersand, even in URL-encoded form, CakePHP will read in the URL incorrectly.</p><p>In my case, I was taking a form submission and redirecting it to a new page, appending the submitted data as URL parameters (e.g. of the form /controller/action/<strong>name:value</strong>). CakePHP handles ampersand in POST-submitted forms just fine, but during the redirect I noticed that the value of the parameter was cut off at the ampersand. For example, I&#8217;m creating a search form for a list of items (located at /items/index). When you submit the form with a value of &#8220;ball &amp; chain&#8221; the controller sees it just fine. The form is submitted to the search action (/items/search) which takes the values, constructs a new URL, and redirects back to the item list (/items/index/Search.keywords:ball+%26+chain). However, the index action only sees &#8220;ball &#8221; &#8230; the rest of the value is assumed to be additional key/value pairs in the querystring.</p><p>This problem seems like a pretty major issue to me. A <a
href="http://www.mail-archive.com/tickets-cakephp@googlegroups.com/msg00775.html">bug report</a> has already been filed, but I don&#8217;t believe it will be addressed anytime soon. And for good reason, the bug is actually in Apache&#8217;s mod_rewrite module rather than in CakePHP. When mod_rewrite processes a URL it unescapes any escaped characters (newer releases unescape to two levels). What this means for CakePHP is that any escaped characters in the URL become normal characters in the querystring (%26 becomes &amp;).</p><p>The best overview of the problem has a good work-around, but it requires modification of the core CakePHP scripts. I&#8217;ll leave that to the experts. On our install I&#8217;ve found that a triple-escaped value will only be unescaped twice, resulting in the proper escaping after being parsed by mod_rewrite.</p><p><strong>References<br
/> </strong></p><ul><li><a
href="http://www.dracos.co.uk/code/apache-rewrite-problem/">The Apache mod_rewrite Problem</a></li><li><a
title="alternate source: http://web.archive.org/web/20090515124524/http://www.usenet-forums.com/apache-web-server/39839-mod_rewrite-ampersand.html" href="http://www.usenet-forums.com/apache-web-server/39839-mod_rewrite-ampersand.html" class="broken_link">mod_rewrite &amp; ampersand</a></li><li><a
href="https://issues.apache.org/bugzilla/show_bug.cgi?id=23295">Escape problem in mod_rewrite [P] action&#8230;</a></li><li><a
href="https://issues.apache.org/bugzilla/show_bug.cgi?id=32328">Make mod_rewrite escaping optional / expose internal map</a></li><li><a
href="http://drupal.org/node/68886">Handle ampersands in search queries and other URLs when clean URLs are on</a></li><li><a
href="http://www.mediawiki.org/wiki/Manual:Short_URL/Ampersand_solution">Short URL/Ampersand solution</a></li></ul> ]]></content:encoded> <wfw:commentRss>http://techlog.p2061.org/2009/01/15/cakephp-apache-and-ampersands/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
