<?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>Cor Cinza &#187; CakePHP</title>
	<atom:link href="http://www.eberfdias.com/blog/category/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.eberfdias.com/blog</link>
	<description>Textos sobre coisas por Éber F. Dias</description>
	<lastBuildDate>Thu, 22 Jul 2010 18:17:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Optionable Behavior</title>
		<link>http://www.eberfdias.com/blog/optionable-behaviour/327/</link>
		<comments>http://www.eberfdias.com/blog/optionable-behaviour/327/#comments</comments>
		<pubDate>Fri, 19 Mar 2010 22:27:53 +0000</pubDate>
		<dc:creator>Éber</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://www.eberfdias.com/blog/?p=327</guid>
		<description><![CDATA[The Optionable Behavior is a CakePHP behavior that tries to bring something from schemaless databases to RDBMS databases like MySQL. With it, you can add as much extra fields as you need to a table without touching the DB design / schema. So, how do I use it? Actually, we will store extra data on [...]]]></description>
			<content:encoded><![CDATA[<p>The Optionable Behavior is a CakePHP behavior that tries to bring something from schemaless databases to RDBMS databases like MySQL. With it, you can add as much extra fields as you need to a table without touching the DB design / schema.</p>
<p><span id="more-327"></span></p>
<h3>So, how do I use it?</h3>
<p>Actually, we will store extra data on another table so, first of all, you need to create this auxiliary table:</p>
<pre>
CREATE TABLE IF NOT EXISTS `options` (
  `id` int(11) NOT NULL auto_increment,
  `model` varchar(32) NOT NULL,
  `related_id` int(11) NOT NULL,
  `opt_key` varchar(32) NOT NULL,
  `opt_value` text NOT NULL,
  PRIMARY KEY (`id`),
  KEY `opt_key` (`opt_key`)
);
</pre>
<p>If you can&#8217;t have a new table named &#8216;options&#8217;, don&#8217;t worry. Just name it something else. Now you need to create the model file for this table. Something like this should be enough:</p>
<pre>
class Option extends AppModel { }
</pre>
<p>If you are using a different table name, don&#8217;t forget to change the class name to whatever other name you have for it. Now you can download the behavior file <a href="http://gist.github.com/334181">here</a>. Place it under your behaviors folder.</p>
<h3>Now what?</h3>
<p>Let&#8217;s pretend that you have a model named <strong>Blog</strong> which has three columns on it&#8217;s table: id, title and content. Generally, if I want to extend this table, I&#8217;ll have to deal with it&#8217;s schema, but using the Optionable Behavior, things get really easy. Imagine that I want to add my <strong>mood</strong> and the <strong>current weather</strong> to some posts on the blog. I just have to do something like this:</p>
<pre>
public $actsAs = array(
	'Optionable' => array(
		'model' => 'Option',
		'fields' => array('mood', 'weather'),
		'emptyFields' => true
	)
);
</pre>
<p>Now I can just put those extra keys on a form, save some data and that is it. The data will be retrieved from the database just like if they were from the Blog&#8217;s schema itself.</p>
<p>See that we have three configuration keys to the behavior:</p>
<ul>
<li><strong>model</strong>: defaults to &#8216;Option&#8217;. Just define this if you are using a different table name.</li>
<li><strong>fields</strong>: no default. Here you can define every optional field that you want to use in your model.</li>
<li><strong>emptyFields</strong>: defaults to true. If true, even if that option is not registered on the auxiliary table, the key will be present on the data array.</li>
</ul>
<p>This behavior tries it&#8217;s best to make every optional field feels like a native one. This means that you can save, validate and retrieve optional data just like you would with common fields. We are working to make it possible to filter data by optional fields as well.</p>
<p>A great benefit of using this behavior is that you can store different data types on the DB seamlessly. Every data is serialized before getting saved to the table. That means that you can store arrays, objects and other data types not natively supported by RDBMSs without having any trouble converting them as they come and go.</p>
<p>Well, I guess this is it. If you have any idea on how to make this work any better, please, share with us! Here is the code:</p>
<p><script src="http://gist.github.com/334181.js?file=optionable.php"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eberfdias.com/blog/optionable-behaviour/327/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Easy sorting with CakePHP</title>
		<link>http://www.eberfdias.com/blog/easy-sorting-with-cakephp/265/</link>
		<comments>http://www.eberfdias.com/blog/easy-sorting-with-cakephp/265/#comments</comments>
		<pubDate>Fri, 19 Mar 2010 19:42:26 +0000</pubDate>
		<dc:creator>Éber</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://www.eberfdias.com/blog/?p=265</guid>
		<description><![CDATA[Hey guys! Here is a simple function that helps me to sort elements in a MySQL database by it&#8217;s ID&#8217;s: If that is usefull to you, let me know!]]></description>
			<content:encoded><![CDATA[<p>Hey guys!</p>
<p>Here is a simple function that helps me to sort elements in a MySQL database by it&#8217;s ID&#8217;s:</p>
<p><script src="http://gist.github.com/249256.js?file=simplesort.php"></script></p>
<p>If that is usefull to you, let me know!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eberfdias.com/blog/easy-sorting-with-cakephp/265/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sluggable Behavior</title>
		<link>http://www.eberfdias.com/blog/sluggable-behavior/210/</link>
		<comments>http://www.eberfdias.com/blog/sluggable-behavior/210/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 00:18:58 +0000</pubDate>
		<dc:creator>Éber</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://www.eberfdias.com/blog/?p=210</guid>
		<description><![CDATA[On the web, &#8220;slug&#8221; is a short text used in a URL to identify and describe a resource. As Mariano Iglesias&#8217; Sluggable Behavior decription says: This is particularly useful to create SEO links out of, for example, a table of articles. Instead of seeing those ugly /articles/view/4 URLs, use the Sluggable Behavior and easily accept [...]]]></description>
			<content:encoded><![CDATA[<p>On the web, &#8220;slug&#8221; is <a href="http://en.wikipedia.org/wiki/Slug_%28typesetting%29#Slug_.28web_publishing.29">a short text used in a URL to identify and describe a resource</a>. As Mariano Iglesias&#8217; Sluggable Behavior decription says:</p>
<blockquote><p>This is particularly useful to create SEO links out of, for example, a table of articles. Instead of seeing those ugly /articles/view/4 URLs, use the Sluggable Behavior and easily accept URLs such as /articles/view/my-seo-friendly-article. It handles the slug creation, slug collision, and allows you to specify different settings such as the separator to use, maximum width of a slug, among other useful parameters.</p></blockquote>
<p>My Sluggable Behavior is a simpler version of <a href="http://cake-syrup.sourceforge.net/ingredients/sluggable-behavior/">Mariano Iglesias&#8217; Sluggable Behavior</a> with a few add-ons. It&#8217;s basically the same thing but instead of implementing all the slug logic on the behavior, it just uses Cake&#8217;s Inflector::slug() method. With CakePHP 1.3, this method is really powerful and <a href="http://book.cakephp.org/view/1579/Library-classes">flexible</a>.</p>
<p><span id="more-210"></span></p>
<p>To use it, just <a href="http://gist.github.com/338096">place the behavior file</a> on the proper place and call it on the model you want to &#8220;slugify&#8221;:</p>
<pre>
var $actsAs = array('Sluggable');
</pre>
<p>By default, this will automatically create slugs from a field named &#8220;title&#8221; and place it under a field named &#8220;slug&#8221;. If you want, you can customize everything. Checkout the configuration keys:</p>
<pre>
var $actsAs = array(
	'Sluggable' => array(
		'fields' => 'title',
		'scope' => false,
		'conditions' => false,
		'slugfield' => 'slug',
		'separator' => '-',
		'overwrite' => false,
		'length' => 256,
		'lower' => true
	)
);
</pre>
<p>Those are all the default values. Let&#8217;s check what they can do, one by one:</p>
<ul>
<li><strong>fields</strong>: as you can see, this is where you define which field will provide the content to the slug. You can inform more than one with an array.</li>
<li><strong>scope</strong>: after generating a slug, the system will check for similar slugs on the database in order to generate a unique slug. By default, it will scan all your registries, but you can define scopes. Example: you want the system to use an already registered slug on a different category. You can define an array like this: &#8216;scope&#8217; => array(&#8216;category_id&#8217;). The system will only check for records where the &#8220;category_id&#8221; is the same from the data being saved.</li>
<li><strong>conditions</strong>: these are extra conditions you can inform. The system will scan for similar slugs previously registered on the database using those conditions as well. They work just like a normal find conditions array.</li>
<li><strong>slugfield</strong>: the name of the field that will store the slug on the database.</li>
<li><strong>separator</strong>: the character that will be used to replace white spaces on the slug.</li>
<li><strong>overwrite</strong>: if true, this will replace the slug from a record on update, otherwise it will be kept untouched on a update.</li>
<li><strong>length</strong>: this defines the length of the slug text.</li>
<li><strong>lower</strong>: if false, the behavior won&#8217;t make the slug all lower case.</li>
</ul>
<p>Once again, this behavior is totally inspired by <a href="http://cake-syrup.sourceforge.net/ingredients/sluggable-behavior/">Mariano Iglesias&#8217; Sluggable Behaviour</a>. If you want to help, please comment! Here is the source code:</p>
<p><script src="http://gist.github.com/338096.js"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eberfdias.com/blog/sluggable-behavior/210/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Fazendo joins on the fly no CakePHP</title>
		<link>http://www.eberfdias.com/blog/fazendo-joins-on-the-fly-no-cakephp/206/</link>
		<comments>http://www.eberfdias.com/blog/fazendo-joins-on-the-fly-no-cakephp/206/#comments</comments>
		<pubDate>Sat, 18 Jul 2009 02:58:13 +0000</pubDate>
		<dc:creator>Éber</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://www.eberfdias.com/blog/?p=206</guid>
		<description><![CDATA[A um tempo atras escrevi este artigo que falava sobre como filtrar uma consulta com associações HABTM no CakePHP. O artigo já tem mais de um ano e nem está mais aqui. Muita coisa já mudou. Na verdade, na época a coisa já havia mudado, mas eu não sabia. Foi quando eu encontrei este outro [...]]]></description>
			<content:encoded><![CDATA[<p>A um tempo atras escrevi este artigo que falava sobre como filtrar uma consulta com associações HABTM no CakePHP. O artigo já tem mais de um ano e nem está mais aqui. Muita coisa já mudou.</p>
<p>Na verdade, na época a coisa já havia mudado, mas eu não sabia. Foi quando eu encontrei este <a href="http://bakery.cakephp.org/articles/view/quick-tip-doing-ad-hoc-joins-in-model-find">outro artigo aqui</a>. O artigo ensina como fazer joins em casos específicos utilizando o Model::find().</p>
<p><span id="more-206"></span></p>
<p>Então imagine que temos duas tabelas, uma de Posts e outra de Tags e exista uma relação HABTM entre elas. Eu quero encontrar todos os posts com a Tag &#8220;design&#8221;, como fazer?</p>
<p>Uma vez dentro do controller dos posts eu posso escrever o seguinte código:</p>
<p><em>Obs.: Este código não foi testado e seu objetivo é apenas o de ilustrar as possibilidades aqui apresentadas.</em></p>
<pre>
$posts = $this->Post->find('all', array(
	'joins' => array(
		array(
			'table'      => 'posts_tags',
			'alias'      => 'PostsTag',
			'type'       => 'INNER',
			'conditions' => array('PostsTag.post_id = Post.id')
		),
		array(
			'table'      => 'tags',
			'alias'      => 'Tag',
			'type'       => 'INNER',
			'conditions' => array(
				'Tag.id = PostsTag.tag_id',
				'Tag.tag LIKE' => '%design%'
			)
		)
	)
));
</pre>
<p>Trazendo um caso da vida real, na <a href="http://www.aquitanda.com/">Quitanda</a> eu queria mostrar os últimos posts de uma <a href="http://www.aquitanda.com/blog/categoria/painel/">determinada categoria</a> dentro do painel dos usuários com lojas. O nosso blog é feito com <a href="http://www.wordpress.org/">WordPress</a>. Eu teria que criar um arquivo model para a tabela de posts do WP para poder fazer isso, mas fiquei com preguiça já que este seria o único lugar onde isto aconteceria, então ao invés de criar um arquivo de modelo, eu criei ele dentro do próprio controller utilizando a classe <a href="http://api.cakephp.org/class/class-registry">ClassRegistry</a> e o método <a href="http://api.cakephp.org/class/class-registry#method-ClassRegistryinit">init</a>.</p>
<p>Como minha tabela do WordPress era diferente da utilizada pela aplicação, primeiro eu defini um novo vetor de configurações no meu arquivo config/database.php. Veja:</p>
<pre>
var $wp = array(
	'driver' => 'mysql',
	'persistent' => false,
	'host' => 'localhost',
	'login' => 'user',
	'password' => 'password',
	'database' => 'qolwp',
	'encoding' => 'utf8'
);
</pre>
<p>Depois, já no meu controller, escrevi a seguinte linha:</p>
<pre>
$this->WpPost = ClassRegistry::init(array(
	'ds' => 'wp',
	'table' => 'wp_posts',
	'class' => 'model',
	'alias' => 'WpPost'
));
</pre>
<p>Os parâmetros praticamente se auto explicam mas, a chave &#8216;ds&#8217; guarda a variável de configuração do banco que vai ser utilizada, no caso &#8216;wp&#8217;, como definido anteriormente. Depois dizemos qual é a tabela a ser utilizada, o tipo de classe a ser registrada e o &#8216;alias&#8217; para ser utilizado nas consultas.</p>
<p>Depois eu fiz a consulta. Ela ficou meio extensa e é claro que escrevendo isto direto no SQL seria muito mais simples, mas fica aqui a titulo de exemplo. Eu fiz isso mais para experimentar as possibilidades e ver o que acontecia. Olha só:</p>
<pre>
$posts = $this->WpPost->find('all', array(
        'joins' => array(
                array(
                        'table' => 'wp_term_relationships',
                        'alias' => 'WpTermRelationship',
                        'type' => 'INNER',
                        'conditions' => array('WpPost.ID = WpTermRelationship.object_id')
                ),
                array(
                        'table' => 'wp_term_taxonomy',
                        'alias' => 'WpTermTaxonomy',
                        'type' => 'INNER',
                        'conditions' => array('WpTermRelationship.term_taxonomy_id = WpTermTaxonomy.term_taxonomy_id')
                ),
                array(
                        'table' => 'wp_terms',
                        'alias' => 'WpTerm',
                        'type' => 'INNER',
                        'conditions' => array('WpTermTaxonomy.term_id = WpTerm.term_id')
                )
        ),
        'conditions' => array(
                'WpPost.post_type' => 'post',
                'WpPost.post_status' => 'publish',
                'WpTermTaxonomy.taxonomy' => 'category',
                'WpTerm.slug' => 'painel'
        ),
        'fields' => array(
                'WpPost.post_title',
                'WpPost.post_content',
                'WpPost.guid',
                'WpPost.post_date'
        ),
        'group' => 'WpPost.ID',
        'order' => 'WpPost.post_date DESC',
        'limit' => 5
));
</pre>
<p>O SQL que esta função vai criar fica assim:</p>
<pre>
SELECT `WpPost`.`post_title`, `WpPost`.`post_content`, `WpPost`.`guid`, `WpPost`.`post_date` FROM `wp_posts` AS `WpPost` INNER JOIN wp_term_relationships AS `WpTermRelationship` ON (`WpPost`.`ID` = `WpTermRelationship`.`object_id`) INNER JOIN wp_term_taxonomy AS `WpTermTaxonomy` ON (`WpTermRelationship`.`term_taxonomy_id` = `WpTermTaxonomy`.`term_taxonomy_id`) INNER JOIN wp_terms AS `WpTerm` ON (`WpTermTaxonomy`.`term_id` = `WpTerm`.`term_id`) WHERE `WpPost`.`post_type` = 'post' AND `WpPost`.`post_status` = 'publish' AND `WpTermTaxonomy`.`taxonomy` = 'category' AND `WpTerm`.`slug` = 'painel' GROUP BY `WpPost`.`ID` ORDER BY `WpPost`.`post_date` DESC LIMIT 5
</pre>
<p>Com isto você já pode ver que existem muitas possibilidades. Por exemplo, você pode fazer consultas com <strong>LEFT JOIN</strong> ao invés de <strong>INNER JOIN</strong> simplesmente trocando a chave &#8216;type&#8217;. Veja também que nas condições gerais da minha consulta eu pude colocar filtros diretamente relacionados às tabelas que eu dei join. No <a href="http://bakery.cakephp.org/articles/view/quick-tip-doing-ad-hoc-joins-in-model-find">artigo original</a> existe uma chave &#8216;foreignKey&#8217; com valor false, mas analisando a API, não achei nenhuma referência a este valor em nenhum dos adaptadores de bancos de dados, então me parece ser algo totalmente dispensavel e a função funciona sem que ela seja declarada.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eberfdias.com/blog/fazendo-joins-on-the-fly-no-cakephp/206/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
