<?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>Jamie Nay &#187; data retrieval</title>
	<atom:link href="http://jamienay.com/tag/data-retrieval/feed/" rel="self" type="application/rss+xml" />
	<link>http://jamienay.com</link>
	<description>A PHP web developer writing about the web.</description>
	<lastBuildDate>Sat, 21 Aug 2010 21:11:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Cipher Behavior with Zend_Filter for CakePHP 1.3 &#8211; Easy Two-Way Encryption</title>
		<link>http://jamienay.com/2010/02/cipher-behavior-with-zend_filter-for-cakephp-1-3-easy-two-way-encryption/</link>
		<comments>http://jamienay.com/2010/02/cipher-behavior-with-zend_filter-for-cakephp-1-3-easy-two-way-encryption/#comments</comments>
		<pubDate>Thu, 25 Feb 2010 04:29:00 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[data retrieval]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=547</guid>
		<description><![CDATA[Here&#8217;s a quickie &#8211; a Cipher behavior for CakePHP to handle two-way encryption of sensitive data. If you want to store, say, credit card information, you&#8217;ll need a way to retrieve it later; Cake&#8217;s built-in security hashing is one-way, meaning that once it&#8217;s encrypted it ain&#8217;t comin&#8217; back. So, I turned to the Zend Framework and brought in Zend_Filter_Encrypt and [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a quickie &#8211; a Cipher behavior for CakePHP to handle two-way encryption of sensitive data. If you want to store, say, credit card information, you&#8217;ll need a way to retrieve it later; Cake&#8217;s built-in security hashing is one-way, meaning that once it&#8217;s encrypted it ain&#8217;t comin&#8217; back. So, I turned to the Zend Framework and brought in Zend_Filter_Encrypt and Zend_Filter_Decrypt to handle the heavy lifting. This behavior uses <strong>mcrypt</strong>, so you need to have that PHP extension installed, which (as far as I can tell) is fairly standard.</p>
<p><strong><a href="http://github.com/jamienay/cipher_behavior">Download the Cipher Behavior Source on Github</a></strong></p>
<p>First things first &#8211; you need to throw a couple of libraries from the Zend Framework into your vendors folder. Yup, I love my Zend! CakePHP teams up very well with ZF.</p>
<p>You&#8217;ll need the following Zend Framework libraries (<a href="http://framework.zend.com/download/latest" target="_blank">download the framework</a>):</p>
<ul>
<li> Zend_Filter (Zend/Filter.php and Zend/Filter)</li>
<li>Zend_Loader (Zend/Loader.php and Zend/Loader)</li>
</ul>
<p>Put those files in your vendors folder. You&#8217;ll also need to update your include_path somewhere (say, app/bootstrap.php) with the following:</p>
<pre>
ini_set('include_path', ini_get('include_path') . ':' . APP . '/vendors');
</pre>
<p>I also recommend defining an autoload function somewhere for Zend classes (again, bootstrap.php is a good choice). A simple, Zend-only autoloader might look like this:</p>
<pre class="brush: php">
function __autoload($path) {
	if (substr($path, 0, 5) == &#039;Zend_&#039;) {
		include str_replace(&#039;_&#039;, &#039;/&#039;, $path) . &#039;.php&#039;;
	}
	return $path;
}
</pre>
<p>The behavior has three config options:</p>
<ul>
<li><strong>key</strong>: the encryption key to use. If null (default), then it&#8217;ll use the value of Configure::read(&#8216;Security.salt&#8217;).</li>
<li><strong>automatic</strong>: whether to automatically encrypt and decrypt data as it&#8217;s saved/retrieved. Defaults to true.</li>
<li><strong>fields</strong>: an array of fields belong to the model that should be encrypted</li>
</ul>
<p>Usage of the behavior itself is straightforward: just add Cipher to the $actsAs array of any model you&#8217;d like:</p>
<pre class="brush: php">
Class User extends AppModel {
    var $actsAs = array(&#039;Cipher&#039;);
}

Class Order extends AppModel {
    var $actsAs = array(&#039;Cipher&#039; =&gt; array(&#039;fields&#039; =&gt; array(&#039;credit_card&#039;, &#039;expiry&#039;)));
}
</pre>
<p>You can also manually decrypt and encrypt data, either by passing an array of find results (in which case the fields specified in &#8216;fields&#8217; will be encrypted/decrypted), or by passing a string:</p>
<pre class="brush: php">
// Manually encrypt/decrypt the results of Model::find()
$encrypted = $this-&gt;encrypt($findResults);
$decrypted = $this-&gt;decrypt($encrypted );

// Manually encrypt/decrypt a string
$encryptedSecretString= $this-&gt;encrypt(&#039;my secret string&#039;);
$decryptedString = $this-&gt;decrypt($encryptedSecretString);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2010/02/cipher-behavior-with-zend_filter-for-cakephp-1-3-easy-two-way-encryption/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Zend_Search_Lucene Datasource for CakePHP</title>
		<link>http://jamienay.com/2010/01/zend_search_lucene-datasource-for-cakephp/</link>
		<comments>http://jamienay.com/2010/01/zend_search_lucene-datasource-for-cakephp/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 21:53:33 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[data retrieval]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=512</guid>
		<description><![CDATA[Major update January 22/10: much of the content of this article has been updated to reflect the changes to the datasource, the latest version of which you can download on Github. Just out of the oven &#8211; a Zend_Search_Lucene datasource for CakePHP (built with 1.2 but probably works just fine in 1.3) that I originally wrote for an in-house CMS [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Major update January 22/10:</strong> much of the content of this article has been updated to reflect the changes to the datasource, the latest version of which you can <a href="http://github.com/jamienay/zend_search_lucene_source">download on Github</a>.</p>
<p>Just out of the oven &#8211; a Zend_Search_Lucene datasource for CakePHP (built with 1.2 but probably works just fine in 1.3) that I originally wrote for an in-house CMS site search plugin. I can&#8217;t release the plugin itself (and there&#8217;s so much CMS-specific code that it would need a lot of work to make it generic anyway), but I thought that someone might find the datasource itself useful. It&#8217;s pretty basic at this point and doesn&#8217;t implement some of the fancier Zend_Search_Lucene features such as sorting (it just returns sorted in score order, which is probably what you want anyway).</p>
<p>Zend_Search_Lucene is a text-based search index system for developers who don&#8217;t want to (or can&#8217;t) use a database for search indexing.</p>
<p><a href="http://github.com/jamienay/zend_search_lucene_source">Download the current version of the ZendSearchLuceneDatsource from my Github repository</a>.</p>
<p>I won&#8217;t go into detail about how to add data into the Lucene database since the <a href="http://framework.zend.com/manual/en/zend.search.lucene.html">Zend Framework documention</a> is so good (CakePHP should be jealous!). You&#8217;ll find all the info you need there. There are also a couple of older articles out there that show how you can integrate Zend_Search_Lucene into CakePHP:</p>
<ul>
<li><a href="http://cakephptips.blogspot.com/2008/04/build-your-own-search-engine-in-php_5060.html">Build your own search engine in PHP</a></li>
<li><a href="http://bakery.cakephp.org/articles/view/integrating-zend-framework-lucene-with-your-cake-application">Integrating Zend Framework Lucene with your Cake Application</a></li>
</ul>
<p><strong>Setup</strong></p>
<p><strong>First</strong>, copy zend_search_lucene.php to models/datasources.</p>
<p>Then, you&#8217;ll need to download the Zend_Search_Lucene library from the Zend Framework website and put some files into your /vendors directory:</p>
<ul>
<li>Zend/Search (the directory and all of its contents)</li>
<li>Zend/Exception.php</li>
</ul>
<p>You&#8217;ll also need to update your include path to include app/vendors, since the Zend Framework loads a lot of classes on its own. I also made a little autoload function to make the loading of Zend Framework classes easier. Put the following code somewhere common, such as app/bootstrap.php:</p>
<pre class="brush: php">
ini_set(&#039;include_path&#039;, ini_get(&#039;include_path&#039;) . &#039;:&#039; . CAKE_CORE_INCLUDE_PATH . DS . &#039;/vendors&#039;);
function __autoload($path) {
if (substr($path, 0, 5) == &#039;Zend_&#039;) {
include str_replace(&#039;_&#039;, &#039;/&#039;, $path) . &#039;.php&#039;;
}
return $path;
}
</pre>
<p>You also need to put the DB config for the datasource in config/database.php (<strong>updated Jan 20/2010 for better DebugKit compatibility</strong>):</p>
<pre class="brush: php">
var $zendSearchLucene = array(
	&#039;datasource&#039; =&gt; &#039;ZendSearchLucene&#039;,
	&#039;indexFile&#039; =&gt; &#039;lucene&#039;, // stored in the cache dir.
	&#039;driver&#039; =&gt; &#039;&#039;,
	&#039;source&#039; =&gt; &#039;search_indices&#039;
);
</pre>
<p>Then, in the model that&#8217;ll act as your search index (say, for example, SearchIndex), specify the DB config:</p>
<pre class="brush: php">
&lt;?php class SearchIndex extends AppModel {
var $useDbConfig = &#039;zendSearchLucene&#039;;
}
?&gt;
</pre>
<p><strong>Saving/Indexing</strong></p>
<p>I&#8217;ve tried to keep the datasource functions as simple and familiar as possible. When saving an item to the index, the datasource expects a multidimensional array for each item. For compatibility with CakePHP&#8217;s datasource code, the &#8216;meat&#8217; of the data is nested in the third level of the array. Each sub-array contains information about a field to be stored. For example:</p>
<pre class="brush: php">
$saveData = array(&#039;SearchIndex&#039; =&gt; array(
  	&#039;document&#039; =&gt; array(
		array(
			&#039;key&#039; =&gt; &#039;name&#039;,
			&#039;value&#039; =&gt; $record[$Model-&gt;alias][$this-&gt;settings[$Model-&gt;alias][&#039;name&#039;]],
			&#039;type&#039; =&gt; &#039;Text&#039;
		),
		array(
			&#039;key&#039; =&gt; &#039;description&#039;,
			&#039;value&#039; =&gt; $record[$Model-&gt;alias][$this-&gt;settings[$Model-&gt;alias][&#039;description&#039;]],
			&#039;type&#039; =&gt; &#039;Text&#039;
		),
		array(
			&#039;key&#039; =&gt; &#039;url&#039;,
			&#039;value&#039; =&gt; $this-&gt;__constructUrl($Model, $record),
			&#039;type&#039; =&gt; &#039;Text&#039;
		)
	)
 ));
</pre>
<p>Passing that data in a Model::save() call will in turn execute the following Zend code (more or less &#8211; this is a very simplified version of the actual ZendSearchLuceneSource saving code):</p>
<pre class="brush: php">
$index = Zend_Search_Lucene::open(&#039;/path/to/the/index/set/in/dbConfig&#039;);
$doc = new Zend_Search_Lucene_Document();
foreach ($data as $field) {
$doc-&gt;addField(Zend_Search_Lucene_Field::$field[&#039;type&#039;]($field[&#039;key&#039;], $field[&#039;value&#039;]));
}
$index-&gt;addDocument($doc);
</pre>
<p>Obviously that&#8217;s a basic example; you&#8217;ll probably send a whole bunch of dynamic info to the indexer. But that&#8217;s the gist of it anyway.</p>
<p><strong>Querying</strong></p>
<p>You can search for records just like you would a regular datasource. Pass the search terms as a &#8220;query&#8221; condition. If you want the search terms to be highlighted in the returned results, pass <strong>&#8216;highlight&#8217; =&gt; true</strong> in the array of options. Note that only indexed fields will be highlighted.</p>
<p>You can find all results:</p>
<pre class="brush: php">
function search($term) {
$results = $this-&gt;SearchIndex-&gt;find(&#039;all&#039;, array(&#039;highlight&#039; =&gt; true, &#039;conditions&#039; =&gt; array(&#039;query&#039; =&gt; &#039;best cakephp tutorials&#039;)));
}
</pre>
<p>You can mimic Google&#8217;s I&#8217;m Feeling Lucky with find(&#8216;first&#8217;):</p>
<pre class="brush: php">
function search($term) {
$topResult = $this-&gt;SearchIndex-&gt;find(&#039;first&#039;, array(&#039;conditions&#039; =&gt; array(&#039;query&#039; =&gt; &#039;best cakephp tutorials&#039;)));
}
</pre>
<p>You can even paginate:</p>
<pre class="brush: php">
function search($term) {
$this-&gt;paginate = array(
&#039;limit&#039; =&gt; 10,
&#039;conditions&#039; =&gt; array(&#039;query&#039; =&gt; &#039;best CakePHP tutorials&#039;),
&#039;highlight&#039; =&gt; true
);

$results = $this-&gt;paginate();
}
</pre>
<p>Results are returned in the expected CakePHP way, as a multidimensional array &#8211; <strong>$results[0]['MyModelAlias']</strong> for multiple records, <strong>$results['MyModelAlias']</strong> for one (i.e. with find(&#8216;first&#8217;)).</p>
<p>There you go &#8211; enjoy! As always, comments and suggestions are welcomed.</p>
<p>I used the <a href="http://blog.loadsys.com/2009/06/19/cakephp-rss-feed-datasource/">RSS Feed datasource</a> by Loadsys as a guide to good datasource design. I may have borrowed a function or two. <img src='http://jamienay.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><a href="http://www.neilcrookes.com/2009/11/21/cakephp-searchable-plugin">Neil Crookes&#8217; Searchable plugin</a> also helped.</p>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2010/01/zend_search_lucene-datasource-for-cakephp/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Finding Random Model Records in CakePHP 1.2</title>
		<link>http://jamienay.com/2009/11/finding-random-model-records-in-cakephp-1-2/</link>
		<comments>http://jamienay.com/2009/11/finding-random-model-records-in-cakephp-1-2/#comments</comments>
		<pubDate>Fri, 27 Nov 2009 23:56:03 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[data retrieval]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://jamienay.com/?p=465</guid>
		<description><![CDATA[There are two main methods for finding random records with PHP (and in this case CakePHP) and MySQL: Use a SELECT query with ORDER BY RAND() and LIMIT x (where x is the number of results you want). Get a list of the primary keys of every record in the table, use PHP to select x random entries from that [...]]]></description>
			<content:encoded><![CDATA[<p>There are two main methods for finding random records with PHP (and in this case CakePHP) and MySQL:</p>
<ol>
<li>Use a SELECT query with ORDER BY RAND() and LIMIT x (where x is the number of results you want).</li>
<li>Get a list of the primary keys of every record in the table, use PHP to select x random entries from that list, and then use a SELECT query using a WHERE primary_key IN (x, y, z) clause.</li>
</ol>
<p>While option #2 might seem to be slower &#8211; two queries <em>and </em>intermediary PHP work! &#8211; but ORDER BY RAND() tends to churn to an almost-grinding halt when queries tables with lots of records. So, #2 it is. And, since we&#8217;re working with CakePHP, we can abstract it to the Model::find() level with a custom __findXX method that I like to call __findRandom(). Note that usage of this function relies on Matt Curry&#8217;s implementation of <a href="http://www.pseudocoder.com/archives/2008/10/24/cakephp-custom-find-types/">custom find methods</a> (I seem to link to that bit of code in every post!).</p>
<p>The basic idea is that two queries are executed: first Model::find-&gt;(&#8216;list&#8217;) generate a list of primary keys, and then either Model::find-&gt;(&#8216;first&#8217;) or Model::find(&#8216;all&#8217;), depending on whether we&#8217;re looking for one or more records.</p>
<p>The code is available in <a href="http://github.com/jamienay/find_random/">my GitHub repository</a>.</p>
<p>Usage is as simple as (using the User model as an example):</p>
<pre class="brush: php">
$record = $this-&gt;User-&gt;find(&#039;random&#039;);
</pre>
<p>That&#8217;ll return one random record from the User model. Like most Model::find functions, you can pass an array as an optional second argument:</p>
<pre class="brush: php">
$records = $this-&gt;User-&gt;find(&#039;random&#039;, array(
    &#039;amount&#039; =&gt; 5,
    &#039;list&#039; =&gt; array(
        &#039;conditions&#039; =&gt; array(&#039;User.active&#039; =&gt; 1)
    ),
    &#039;find&#039; =&gt; array(
        &#039;contain&#039; =&gt; array(&#039;Group&#039;)
    )
));
</pre>
<p>As you can see, you&#8217;re actually working with two nested arrays for your query modifiers, <strong>&#8216;list&#8217; </strong>and <strong>&#8216;find&#8217;</strong>. Use these arrays to modify the the find(&#8216;list&#8217;) and find(&#8216;first&#8217;)/find(&#8216;all&#8217;) queries respectively. Use the &#8216;<strong>amount&#8217;</strong> option, which defaults to 1, to specify the maximum records you want returned.</p>
<p>You can also bypass the find(&#8216;list&#8217;) query entirely by passing an array of primary keys as the <strong>&#8216;suppliedList&#8217;</strong> argument. For example:</p>
<pre class="brush: php">
$records = $this-&gt;User-&gt;find(&#039;random&#039;, array(
    &#039;amount&#039; =&gt; 5,
    &#039;suppliedList&#039; =&gt; $myIDs,
    &#039;find&#039; =&gt; array(
        &#039;contain&#039; =&gt; array(&#039;Group&#039;)
    )
));
</pre>
<p>As you can see, it&#8217;s a fairly simple function. But, it gets the job done! Here&#8217;s the source code, which, as mentioned above, you can grab on <a href="http://github.com/jamienay/find_random/">GitHub</a> as well:</p>
<pre class="brush: php">
&amp;lt;?php
class AppModel extends Model {

    function find($type, $options = array()) {
        $method = null;
        if (is_string($type)) {
            $method = sprintf(&#039;__find%s&#039;, Inflector::camelize($type));
        }

        if ($method &amp;&amp; method_exists($this, $method)) {
            $results = $this-&gt;{$method}($options);
        } else {
            $args = func_get_args();
            $results = call_user_func_array(array(&#039;parent&#039;, &#039;find&#039;), $args);
        }

        return $results;
    }

    /**
     * __findRandom()
     *
     * Find a list of records ordered by rank.
     * Instead of executing a __findList() query to get the list of IDs,
     * you can pass an array of IDs via the $options[&#039;suppliedList&#039;]
     * argument.
     *
     * Two queries are executed, first a find(&#039;list&#039;) to generate a list of primary
     * keys, and then either a find(&#039;all&#039;) or find(&#039;first&#039;) depending on the return
     * amount specified (default 1).
     *
     * Pass find options to each query using the $options[&#039;list&#039;] and $options[&#039;find&#039;]
     * arguments.
     *
     * Specify $options[&#039;amount&#039;] as the maximum number of random items that should
     * be returned.
     *
     * If you already have an array of IDs(/primary keys), you can skip the find(&#039;list&#039;)
     * query by passing the array as $options[&#039;suppliedList&#039;].
     *
     * @access  private
     * @param   $options  array of standard and function-specific find options.
     * @return  array
     */
    function __findRandom($options = array()) {
        if (!isset($options[&#039;amount&#039;])) {
            $amount = 1;
        } else {
            $amount = $options[&#039;amount&#039;];
        }

        $findOptions = array();
        if (isset($options[&#039;find&#039;])) {
            $findOptions = array_merge($findOptions, $options[&#039;find&#039;]);
        }

        if (!isset($options[&#039;suppliedList&#039;])) {
            $listOptions = array();
            if (isset($options[&#039;list&#039;])) {
                $listOptions = array_merge($listOptions, $options[&#039;list&#039;]);
            }

            $list = $this-&gt;find(&#039;list&#039;, $listOptions);
        } else {
            $list = $options[&#039;suppliedList&#039;];
            $list = array_flip($list);
        }        

        // Just a little failsafe.
        if (count($list) &amp;lt; 1) {
            return $list;
        }

        $originalAmount = null;
        if ($amount &gt; count($list)) {
            $originalAmount = $amount;
            $amount = count($list);
        }

        $id = array_rand($list, $amount);

        if (is_array($id)) {
            shuffle($id);
        }

        if (!isset($findOptions[&#039;conditions&#039;])) {
            $findOptions[&#039;conditions&#039;] = array();
        }

        $findOptions[&#039;conditions&#039;][$this-&gt;alias.&#039;.&#039;.$this-&gt;primaryKey] = $id;
        if ($amount == 1 &amp;&amp; !$originalAmount) {
            return $this-&gt;find(&#039;first&#039;, $findOptions);
        } else {
            return $this-&gt;find(&#039;all&#039;, $findOptions);
        }
    }
}
?&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jamienay.com/2009/11/finding-random-model-records-in-cakephp-1-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
