<?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>WEB рукоделие &#187; CakePHP</title>
	<atom:link href="http://www.handmadesite.net/topics/cakephp/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.handmadesite.net</link>
	<description>Какой механизм?! Все вручную!</description>
	<lastBuildDate>Sun, 13 Nov 2011 23:18:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Особенности межплагинного взаимодействия</title>
		<link>http://www.handmadesite.net/2010/11/osobennosti-mezhplaginnogo-vzaimodejstviya/</link>
		<comments>http://www.handmadesite.net/2010/11/osobennosti-mezhplaginnogo-vzaimodejstviya/#comments</comments>
		<pubDate>Sun, 07 Nov 2010 11:08:30 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Models]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Модели]]></category>
		<category><![CDATA[Модель]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=392</guid>
		<description><![CDATA[Конечно, вызывать методы классов плагина из другого плагина не очень хорошо. Но, тем не менее. Чтобы впоследствии не было мучительно больно, при объявлении связей между моделями, надо всегда указывать свойство className, причем полностью, т.е. &#8216;Plugin.MyModel&#8217;. Иначе возможны весьма забавные эффекты. Справедливо для версий 1.2 и 1.3. Дальше немного кода. Есть два плагина: PluginOne и PluginTwo. [...]]]></description>
			<content:encoded><![CDATA[<p>Конечно, вызывать методы классов плагина из другого плагина не очень хорошо. Но, тем не менее. Чтобы впоследствии не было мучительно больно, при объявлении связей между моделями, надо всегда указывать свойство <var>className</var>, причем полностью, т.е. &#8216;Plugin.MyModel&#8217;. Иначе возможны весьма забавные эффекты. Справедливо для версий 1.2 и 1.3. Дальше немного кода.</p>
<p><span id="more-392"></span>Есть два плагина: <var>PluginOne</var> и <var>PluginTwo</var>. У первого две связанные модели: <var>PluginOneModelOne belongsTo PluginOneModelTwo</var>. У второго плагина одна модель, связанная с первой моделью первого плагина (пусть тоже будет belongsTo, неважно): <var>PluginTwoModelOne belongsTo PluginOneModelOne</var>. Соответственно, объявлено все это вот так:</p>
<p>Файл /plugins/plugin_one/models/plugin_one_model_one.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> PluginOneModelOne <span style="color: #000000; font-weight: bold;">extends</span> PluginOneAppModel <span style="color: #009900;">&#123;</span>
&nbsp;
	<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;">'PluginOneModelTwo'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Файл /plugins/plugin_one/models/plugin_one_model_two.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> PluginOneModelTwo <span style="color: #000000; font-weight: bold;">extends</span> PluginOneAppModel <span style="color: #009900;">&#123;</span>
	<span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$actsAs</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Tree'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Файл /plugins/plugin_two/models/plugin_two_model_one.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> PluginTwoModelOne <span style="color: #000000; font-weight: bold;">extends</span> PluginTwoAppModel <span style="color: #009900;">&#123;</span>
&nbsp;
	<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;">'PluginOneModelOne'</span><span style="color: #339933;">=&gt;</span>array<span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'className'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'PluginOne.PluginOneModelOne'</span>
		<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Создадим в контроллере второго плагина тестовый метод:</p>
<p>Файл /plugins/plugin_two/controllers/plugin_two_model_ones_controller.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> PluginTwoModelOnesController <span style="color: #000000; font-weight: bold;">extends</span> PluginTwoAppController <span style="color: #009900;">&#123;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">function</span> typeofmodel<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #990000;">die</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">get_class</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">PluginTwoModelOne</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">PluginOneModelOne</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">PluginOneModelTwo</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Опачки! Третья модель типа <var>AppModel</var>, а не <var>PluginOneModelTwo</var>! Соответственно, не загружаются behaviours, перечисленные в <var>$actsAs</var>, не работают методы модели, не обрабатываются правила валидации. Хитрый Cake молча создает объект базового класса, чтоб связь работала для стандартной выборки, записи и все.</p>
<p>Достаточно в описании связи моделей первого плагина полностью указать какой класс использовать — все отлично начинает работать. Меняем код <var>PluginOneModelOne</var></p>
<p>Файл /plugins/plugin_one/models/plugin_one_model_one.php</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> PluginOneModelOne <span style="color: #000000; font-weight: bold;">extends</span> PluginOneAppModel <span style="color: #009900;">&#123;</span>
&nbsp;
	<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;">'PluginOneModelTwo'</span><span style="color: #339933;">=&gt;</span>array<span style="color: #009900;">&#40;</span>
				<span style="color: #0000ff;">'className'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'PluginOne.PluginOneModelTwo'</span>
		<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>и запускаем снова тестовый метод. Вот теперь все как и ожидается.</p>
<p>Если подумать, то поведение фреймворка в общем-то логичное. Он ищет класс в текущем плагине и у самого приложения. Хотя мог бы и предупреждать, пусть бы и только в отладочном режиме, что есть проблема.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2010/11/osobennosti-mezhplaginnogo-vzaimodejstviya/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Список всех-всех контроллеров</title>
		<link>http://www.handmadesite.net/2009/11/all-controllers-of-app/</link>
		<comments>http://www.handmadesite.net/2009/11/all-controllers-of-app/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 10:58:38 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=344</guid>
		<description><![CDATA[Понадобилось мне получить список всех контроллеров приложения, включая контроллеры плагинов. Configure::listObjects, увы, такого не умеет. Этот метод выдает только все контроллеры приложения, но без контроллеров плагинов. Пришлось немного адаптировать метод, предложенный Rob Weaver в гуглогруппе CakePHP. На выходе у моего метода получается объединенный список контроллеров. Основные &#8212; как отдает метод listObjects, а принадлежащие плагинам в [...]]]></description>
			<content:encoded><![CDATA[<p>Понадобилось мне получить список всех контроллеров приложения, включая контроллеры плагинов. Configure::listObjects, увы, такого не умеет. Этот метод выдает только все контроллеры приложения, но без контроллеров плагинов. Пришлось немного адаптировать метод, <a href="http://groups.google.com/group/cake-php/browse_thread/thread/ba66cb8ff0d60957/0f7a7d48ac988c32?lnk=gst&amp;q=listobjects+plugin+controller#0f7a7d48ac988c32" target="_blank">предложенный</a> <a href="http://groups.google.com/groups/profile?enc_user=aYwLfxIAAAAlIZ7IWf4E4HkoLpEPQXFa8rhlH0Pnl47z4AZhN98BFg" target="_blank">Rob Weaver</a> в <a href="http://groups.google.com/group/cake-php" target="_blank">гуглогруппе CakePHP</a>. На выходе у моего метода получается объединенный список контроллеров. Основные &#8212; как отдает метод listObjects, а принадлежащие плагинам в виде &laquo;Plugin.Controller&raquo;. Можно их поочередно скармливать сразу в App::import. Разве что выбрость те, названия которых на &laquo;App&raquo; заканчивается, если они не нужны. Будете загружать, помните, что <a href="http://www.handmadesite.net/2009/11/loding-controllers/">класс &laquo;Controller&raquo; уже должен быть загружен</a>.</p>
<p><span id="more-344"></span>
<p><strong>Update.</strong> Метод Folder::findRecursive возвращал слишком много файлов, в том числе и ненужных. Переписал немного, теперь опрашиваем только директорию с плагином и его контроллерами.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #009933; font-style: italic;">/**
 * Lists the names of controllers including plugin controllers
 * 
 * @return array
 */</span>
static <span style="color: #000000; font-weight: bold;">function</span> listControllers<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Core'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'File'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'Folder'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$controllers</span> <span style="color: #339933;">=</span> Configure<span style="color: #339933;">::</span><span style="color: #004000;">listObjects</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'controller'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$config</span> <span style="color: #339933;">=</span> Configure<span style="color: #339933;">::</span><span style="color: #004000;">getInstance</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$config</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">pluginPaths</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$pluginPath</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$folder</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Folder<span style="color: #009900;">&#40;</span><span style="color: #000088;">$pluginPath</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #990000;">list</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$plugin_dirs</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">read</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$plugin_dirs</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$plugin_dir</span><span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$pluginName</span> <span style="color: #339933;">=</span> Inflector<span style="color: #339933;">::</span><span style="color: #004000;">camelize</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$plugin_dir</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cd</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$pluginPath</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$plugin_dir</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$controllerFiles</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'.*_controller\.php'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cd</span><span style="color: #009900;">&#40;</span>Folder<span style="color: #339933;">::</span><span style="color: #004000;">addPathElement</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">path</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'controllers'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000088;">$controllerFiles</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$controllerFiles</span><span style="color: #339933;">,</span><span style="color: #000088;">$folder</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'.*_controller\.php'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$controllerFiles</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span>
            <span style="color: #009900;">&#123;</span>
                <span style="color: #000088;">$file</span> <span style="color: #339933;">=</span> <span style="color: #990000;">basename</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                <span style="color: #000088;">$controllers</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$pluginName</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'.'</span> <span style="color: #339933;">.</span>
                    Inflector<span style="color: #339933;">::</span><span style="color: #004000;">camelize</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #990000;">strlen</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-</span>
                    <span style="color: #990000;">strlen</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'_controller.php'</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;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$controllers</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/11/all-controllers-of-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Контроллеры: загрузка</title>
		<link>http://www.handmadesite.net/2009/11/loding-controllers/</link>
		<comments>http://www.handmadesite.net/2009/11/loding-controllers/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 17:50:02 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Console]]></category>
		<category><![CDATA[Controller]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=332</guid>
		<description><![CDATA[Удивительно но факт. Если из консольного, например, приложения нужно попользоваться моделью, то, само собой, надо загрузить класс модели. App::import&#40;'Model', 'MyModel'&#41;; Но загрузить контроллер так не получится. Получим сообщение об ошибке из-за невозможности найти базовый класс Controller. Вот найти класс Model кейк может, а Controller &#8211; нет. Во всяком случае 1.2.5. В версии 1.3 не пробовал. [...]]]></description>
			<content:encoded><![CDATA[<p>Удивительно но факт. Если из консольного, например, приложения нужно попользоваться моделью, то, само собой, надо загрузить класс модели.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Model'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'MyModel'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Но загрузить контроллер так не получится. Получим сообщение об ошибке из-за невозможности найти базовый класс Controller. Вот найти класс Model кейк может, а Controller &#8211; нет. Во всяком случае 1.2.5. В версии 1.3 не пробовал.</p>
<p>Поэтому приходится загружать его явно и напрямую:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Core'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'Controller'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Controller'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'MyController'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/11/loding-controllers/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Компоненты: перезагрузка</title>
		<link>http://www.handmadesite.net/2009/10/reload-components/</link>
		<comments>http://www.handmadesite.net/2009/10/reload-components/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 22:02:40 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Component]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=324</guid>
		<description><![CDATA[Не знаю, такую-ли ситуацию имел в виду BorisPlus в своем комментарии. Ну, чем богаты. Вообще не хотел это все описывать, потому как код довольно халтурно написан. Этот код приложения, изначально крутившийся по Cake 1.1, был переписан для какой-то беты Cake 1.2. С текущей версией, 1.2.5, он работает без проблем, но, возможно, нуждается в чистке. Эта [...]]]></description>
			<content:encoded><![CDATA[<p>Не знаю, такую-ли ситуацию имел в виду <a href="http://www.handmadesite.net/2008/12/m-for-model/comment-page-1/#comment-794">BorisPlus в своем комментарии</a>. Ну, чем богаты. Вообще не хотел это все описывать, потому как код довольно халтурно написан.</p>
<p>Этот код приложения, изначально крутившийся по Cake 1.1, был переписан для какой-то беты Cake 1.2. С текущей версией, 1.2.5, он работает без проблем, но, возможно, нуждается в чистке.</p>
<p>Эта часть программы выполняющий импорт данных от поставщиков. Поставщики предоставляют данные в CSV формате, но порядок колонок, некоторые значения и т.д., конечно разные. Для осмысленной обработки эти полученные данные надо привести к единообразному виду.</p>
<p>Данные поступают из написанного куцего <em>Datasource</em>, скармливанются компоненту, который и выполняет разбор и приведение к общему виду, потом записываются в нашу таблицу.</p>
<p><span id="more-324"></span></p>
<p>Компонент подгружается динамически, в зависимости от поставщика. Все компоненты являются наследниками базового и реализуют метод <strong>parse</strong>. Этот метод базового компонента, если не перегружен, возвращает <em>NULL</em> &#8212; в этом случае в нашу таблицу ничего не записываем, переходим к следующей порции данных. Соответственно методы наследников тоже могут возвращать <em>NULL</em> при ошибке разбора, например, при отсутствии какого-либо ключевого поля.</p>
<p>Еще базовый компонент содержит некоторое количество служебных методов, общих для всех, с целью унифицировать данные.</p>
<p>Увы, все файлы с компонентами находятся в каталоге <em>components</em>. Зато после перегрузки можно к загруженному компоненту обращаться по имени базового:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">SupplierCsv</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">parse</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$data</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>В коде контроллера загружается базовый компонент. Так как он нужен только в одном методе контроллера, в нем я его и загружаю:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Component'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'SupplierCsv'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #990000;">die</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Cannot load SupplierCsv component'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Еще у базового компонента есть метод <strong>factory</strong>. Я не помню, почему я его статическим объявил, честно. :-) Он получает в качестве параметра ссылку на контроллер и название компонента для обработки данных от конкретного поставщика:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">SupplierCsvComponent<span style="color: #339933;">::</span><span style="color: #004000;">factory</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'CsvComponentName'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$component_name</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Теперь методы базового компонента, <em>SupplierCsvComponent</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> startup<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span><span style="color: #000088;">$controller</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">controller</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$controller</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$controller</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">SupplierCsv</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
static <span style="color: #000000; font-weight: bold;">function</span> factory<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span><span style="color: #000088;">$controller</span><span style="color: #339933;">,</span> <span style="color: #000088;">$supplier</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    App<span style="color: #339933;">::</span><span style="color: #004000;">import</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Component'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$supplier</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'CsvComponentName'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$componentclass</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$supplier</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'CsvComponentName'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">'Component'</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$controller</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">SupplierCsv</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000088;">$componentclass</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$controller</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">SupplierCsv</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">startup</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$controller</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>ну и код метода <strong>startup</strong> дочерних классов:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> startup<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span><span style="color: #000088;">$controller</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    parent<span style="color: #339933;">::</span><span style="color: #004000;">startup</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$controller</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">init</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">name</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Решение неидельное, наверняка можно соптимизировать. Мне, честно говоря, заниматься этим не хочется. Просто потому, что я в итоге начну вообще все с нуля переделывать и менять логику. :)</p>
<p>Откровенно говоря, огород можно было бы и не городить. Можно через App::import() загрузить класс компонента, а потом какой-нибудь переменной контроллера присвоить экземпляр этого класса.</p>
<p>Наверное, я раньше грузил компонент, указывая его в массиве $components, потому и сделал такую навернутую схему.</p>
<p>А может мне не хотелось require_once использовать&#8230; Ну не помню. :)</p>
<p><em>Update:</em> Если соберусь переписывать, надо будет рефлексию попробовать использовать. :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/10/reload-components/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Кэш и консоль</title>
		<link>http://www.handmadesite.net/2009/09/cache-and-console/</link>
		<comments>http://www.handmadesite.net/2009/09/cache-and-console/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 16:41:48 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[XCache]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=321</guid>
		<description><![CDATA[За полноценный пост не считается. Так, узелок на память. В качестве кэша байткода и переменных я использую XCache. Но при запуске консольных приложений, он у меня не работает. Должен или нет, не знаю, не разбирался. Наверное, не должен, если подумать. :-) Просто отметил, что консольные приложения Cake, включая &#8216;cake bake&#8216; высыпают кучу ошибок, если XCache [...]]]></description>
			<content:encoded><![CDATA[<p>За полноценный пост не считается. Так, узелок на память.</p>
<p>В качестве кэша байткода и переменных я использую <a href="http://xcache.lighttpd.net/">XCache</a>. Но при запуске консольных приложений, он у меня не работает. Должен или нет, не знаю, не разбирался. Наверное, не должен, если подумать. :-) Просто отметил, что консольные приложения Cake, включая &#8216;<em>cake bake</em>&#8216; высыпают кучу ошибок, если XCache используется, как кэш по умолчанию. Поэтому в конфигурации кэша приложения (APP/config/core.php) на CakePHP добавляю маленькую проверку на тип API.</p>
<p>Вот как-то так:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PHP_SAPI <span style="color: #339933;">==</span> <span style="color: #0000ff;">'cli'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  Cache<span style="color: #339933;">::</span><span style="color: #004000;">config</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'default'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'engine'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'File'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
  Cache<span style="color: #339933;">::</span><span style="color: #004000;">config</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'default'</span><span style="color: #339933;">,</span>
    <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'engine'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'Xcache'</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'prefix'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'mypfx_'</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'user'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'IamAdmin'</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'password'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'c00leztPass'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Можно пользоваться константой <a href="http://php.net/manual/en/reserved.constants.php#reserved.constants.core">PHP_SAPI</a> или функцией <a href="http://php.net/php_sapi_name">php_sapi_name()</a>, не важно.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/09/cache-and-console/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>CakePHP 1.2, Content-type, debug, RequestHandler и все-все-все</title>
		<link>http://www.handmadesite.net/2009/09/cakephp-1-2-content-type-debug-requesthandler-ftd-so-on/</link>
		<comments>http://www.handmadesite.net/2009/09/cakephp-1-2-content-type-debug-requesthandler-ftd-so-on/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 12:46:16 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[parseExtensions]]></category>
		<category><![CDATA[RequestHandler]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=305</guid>
		<description><![CDATA[Как правильно, с точки зрения CakePHP, отвечать на запросы, если ответ требуется не html, а, например, json? Я считаю, что для получения ответа в нужном формате надо воспользоваться компонентом RequestHandler и указать в запросе расширение, в нашем случае &#8216;.json&#8217;. URL для запроса получится какой-то такой: http://www.oursite.com/controller/action.json Чтобы Cake не пугался этого расширения и вызывал правильное [...]]]></description>
			<content:encoded><![CDATA[<p>Как правильно, с точки зрения CakePHP, отвечать на запросы, если ответ требуется не html, а, например, json? Я считаю, что для получения ответа в нужном формате надо воспользоваться компонентом <em>RequestHandler</em> и указать в запросе расширение, в нашем случае &#8216;.json&#8217;. URL для запроса получится какой-то такой:</p>
<pre>http://www.oursite.com/controller/action.json</pre>
<p>Чтобы Cake не пугался этого расширения и вызывал правильное действие (action), в файл APP/config/routes.php добавим строку:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Router<span style="color: #339933;">::</span><span style="color: #004000;">parseExtensions</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'json'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p><span id="more-305"></span>Теперь надо разобраться с разными представлениями наших данных, чтобы по запросам к /action и /action.json возвращались разные виды. Для этого нужен компонент <em>RequestHandler</em>. Подключим его либо в самом контроллере, либо в родительском AppController:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$components</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'RequestHandler'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Теперь шаблоны для view и layout Cake будет искать в поддиректориях /json. Т.е для layout надо будет создать файл /views/layout/json/default.ctp:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span> <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$content_for_layout</span><span style="color: #339933;">;</span> <span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>и файл для действия /views/controller/json/action.ctp. Какой-то такой:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span> <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$javascript</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">object</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$data</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Это все и без меня все знают, ничего нового тут нет. Разве что <a href="http://cakebaker.42dh.com/2009/07/02/default-views-for-extensions/">cakebaker советует воспользоваться Zend::Json вместо стандартного помощника Javascript</a>, дескать Zend&#8217;овская библиотека автоматом кодирует некоторые спецсимволы.</p>
<p>Но есть одна тонкость, недостаточно отраженная в документации &#8212; заголовок ответа Content-type. По всем ожиданиям он должен быть &#8216;application/json&#8217;, как это определено в коде компонента <em>RequestHandler</em>.</p>
<p>Однако, если включен режим отладки, т.е. <em>Configure.debug &gt; 0</em>, то <em>RequestHandler</em> устанавливает Сontent-Type в text/html без вариантов. Это, в общем-то описано в документации и докопаться до этого можно.</p>
<p>Но совершенно неочевидно, что если режим отладки выключить в коде самого action, написав</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Configure<span style="color: #339933;">::</span><span style="color: #004000;">write</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'debug'</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>то это ничего уже не изменит! <em>RequestHandler</em> устанавливает заголовок во время загрузки компонента. Его метод <em>startup()</em> вызывает метод <em>renderAs()</em>, который вызывает метод <em>respondAs()</em>. Таким образом шаблоны подключаются нужные, те, которые из директорий /json, а Content-Type неправильный.</p>
<p>Лично я, пока, в acton, который должен отдавать данные в json, явно вызывываю <em>RequestHandler-&gt;respondAs()</em> повторно:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>Configure<span style="color: #339933;">::</span><span style="color: #004000;">read</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    Configure<span style="color: #339933;">::</span><span style="color: #004000;">write</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'debug'</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">RequestHandler</span><span style="color: #339933;">-&gt;</span>__responseTypeSet <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">RequestHandler</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">renderAs</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'json'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Это заставляет <em>RequestHandler</em> изменить заголовок Content-type еще раз. Можно похожий код, с проверкой типа запроса и установкой режима отладки и заголовков попробовать запихнуть, скажем, в <em>beforeRender</em>. Обратите внимание на грязный хак с установкой свойства __responseTypeSet в NULL. Метод respondAs сохраняет в эту переменную тип ответа и повторно не будет срабатывать, если он уже был вызван.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/09/cakephp-1-2-content-type-debug-requesthandler-ftd-so-on/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Производительность pagination в CakePHP 1.2</title>
		<link>http://www.handmadesite.net/2009/02/improving-pagination-performance-in-cakephp-12/</link>
		<comments>http://www.handmadesite.net/2009/02/improving-pagination-performance-in-cakephp-12/#comments</comments>
		<pubDate>Sun, 15 Feb 2009 17:50:46 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Models]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[tips and tricks]]></category>
		<category><![CDATA[Модели]]></category>
		<category><![CDATA[Модель]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=253</guid>
		<description><![CDATA[Как влиять на метод контроллера paginate? Можно-ли сделать выборку более оптимальной и как? Много букв о тюнинге постраничных запросов могут быть любопытны более-менее продвинутым программистам на CakePHP]]></description>
			<content:encoded><![CDATA[<p>Хотя использование разбивки на страницы (pagination) вполне заслуживает отдельного, обстоятельного поста, хочу остановиться лишь на паре не слишком очевидных моментов. Я предполагаю, что у читающих этот пост есть определенный навык использования CakePHP, моделей вообще и pagination в частности. Этот пост и так получается длинным.</p>
<p>Известно, что метод контроллера <em>paginate()</em> вызывает последовательно два метода модели &#8212; первый для подсчета общего числа записей, удовлетворяющих заданным условиям, и второй на выборку указанного числа записей начиная с заданной. Т.е. первый это фактически функция SQL COUNT(*), второй &#8212; SELECT &#8230; LIMIT n,m.</p>
<p>Именно из-за того, что вызовов методов больше одного, не срабаывает временная привязка/отвязка подчиненных моделей с помощью <em>bindModel()</em>/<em>unbindModel()</em>. Этим методам, для корректной работы с <em>paginate()</em> приходится передавать второй параметр, равный <em>false</em>. К счастью, еть ContainableBehavior, решающий эту проблему.</p>
<p>Эти запросы не всегда оптимальны и есть возможность улучшить производительность метода <em>paginate()</em> именно за счет оптимизации собственно самих запросов.<span id="more-253"></span></p>
<p>Для того, чтобы влиять на работу метода paginate(), в контроллере CakePHP предусмотрены вызовы двух специальных методов модели: <em>Model::paginateCount()</em> и <em>Model::paginate()</em>. Метод <em>paginate()</em> <strong>контроллера</strong> ищет у модели указанные методы и, если они определены, вызывает их. Если у модели методы не определены, вызываются стандартные <em>Model::find(&#8216;count&#8217; &#8230;)</em> и <em>Model::find(&#8216;all&#8217;,..)</em>, которым в качестве параметров передаются условия выборки, условия для Containable и все прочее.</p>
<p>Понятно, что определив эти методы в модели, все вместе или любой на выбор, можно творить все, что захочется. А сам метод <em>Controller::paginate()</em> (не путайте с нашим <em>Model::paginate()</em>!) тогда будет заниматься исключительно вспомогательной работой &#8212; опредять параметры для LIMIT и настраивать переменные вида (View). Самое главное, чтобы наш метод Model::paginateCount() возвращал (int) количество записей, а Model::paginate() &#8212; массив записей.</p>
<p>Все это предоставляет большое пространство для творчества и гибкость при каких-нибудь навернутых запросах. Но не только. Этими методами можно воспользоваться для оптимизации запросов, которые CakePHP мог бы и самостоятельно составлять.</p>
<p><strong>Model::paginateCount</strong></p>
<p>Первым займемся методом для подсчета количества записей. Этот метод <a href="http://kossak.blogspot.com/2007/11/pagination-cakephp-12.html" target="_blank">я уже описывал чуть более года назад</a>, но то описание во-первых устарело, потому, что в итоге в CakePHP сменился синтаксис вызова COUNT(*), а во-вторых здесь я постараюсь дать более развернутое описание.</p>
<p>При вызове метода Model::paginateCount(), контроллер передает ему три параметра: $conditions, $recursive и $extra.</p>
<ul>
<li>(array) <strong>conditions</strong> &#8212; ассоциативный массив условий, такой же, как массивы условий для любых других методов модели. В нашем случае в него копируются условия из массива Controller::paginate['conditions'].</li>
<li>(int) <strong>recursive</strong> &#8212; значение параметра $recursive. Если это значение установлено в переменной Controller::raginate['recursive'], то оно. Если нет, то берется из значения свойства Model::recursive</li>
<li>(array) <strong>extra</strong> &#8212; ассоциативный массив всех остальных переменных, передаваемых в Controller::paginate, &#8216;contain&#8217; (для ContainableBehavior), &#8216;fields&#8217; и т.д. В общем, содержимое переменной Controller::paginate без первых двух параметров.</li>
</ul>
<p>Хочу отметить, что никто не запрещает указывать в массиве Controller::paginate какие-то свои собственные ключи. Так что, если в методе нашего контроллера определить что-то типа:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">paginate</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'my-cool-param'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000ff;">'YES!'</span><span style="color: #339933;">;</span></pre></div></div>

<p>то эта пара ключ-значение тоже попадет в параметр <strong>extra</strong>.</p>
<p>Чаще всего для разбивки на страницы используется модель, связанная с другими. Классический пример с моделью Article, которая принадлежит (belongsTo) модели Author. Для окончательного вывода нам нужны данные из обоих моделей, но для подсчета записей достаточно посчитать только записи в Article. Т.е. для подсчета строк достаточно выполнить COUNT(*) только для таблицы articles, без лишних JOIN&#8217;ов. MySQL работает гораздо веселее, когда удается обойтись без связанных таблиц. Для этого простого случая вполне можно вызывать $this-&gt;Article-&gt;unbindModel(array(&#8216;belongsTo&#8217;=&gt;array(&#8216;Author&#8217;)))) и связанная модель Author не будет участвовать в подсчете, но будет в выборке результатов. Помните, что метод <em>unbindModel()</em> по умолчанию «отвязывает» модель только на один, следующий, запрос?</p>
<p>Но если связанных моделей много, что, надо помнить их все? Да и дергать unbindModel() перед Controller::paginate() что-то не улыбается. Особенно с точки зрения парадигмы «тонкий контроллер, толстая модель».</p>
<p>Иными словами, я считаю, что надо написать такой метод paginateCount, который будет подсчитывать записи в таблице без присоединения зависимых моделей. Сделать это можно жестко задавая параметр $recursive при вызове <em>Model::find(&#8216;count&#8217;&#8230;)</em>. Получится примерно такой код:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> paginateCount<span style="color: #009900;">&#40;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span> <span style="color: #000088;">$recursive</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'count'</span><span style="color: #339933;">,</span>
        <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
            <span style="color: #0000ff;">'conditions'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'recursive'</span><span style="color: #339933;">=&gt;-</span><span style="color: #cc66cc;">1</span>
        <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Теперь при вызове COUNT(*) не будет совсем никаких JOIN&#8217;ов, несмотря ни на какие отношения.</p>
<p>Я довольно долго пользовался этим вариантом метода и быль вполне им доволен. Но совсем недавно возникла необходимость все-таки в полном подсчете записей &#8212; из-за условия отбора по полю присоединенной модели. Совсем убирать этот метод мне не хотелось и я решил просто добавить возможность указания &#8212; надо ли использовать для подсчета запрос с присоединенными моделями или можно обойтись подсчетом записей только в одной. Если конкретнее, то для frontend метода index использую «быстрый» метод подсчета, а в админской части есть более сложные фильтры, требующие использования «медленного» метода. Чтобы указать необходимость использования «медленного» метода, с использованием присоединенных моделей, используется дополнительный ключ в массиве <em>Controller::paginate</em>. Что я, зря, чтоль столько букв про $extra написал? :-) Теперь если задан сложный фильтр по записям, кроме условий в Controller::paginate['conditions'], добавляю ключ Controller::paginate['count_recursive']=1. А метод стал выглядить так:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> paginateCount<span style="color: #009900;">&#40;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span> <span style="color: #000088;">$recursive</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</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: #339933;">@</span><span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'count_recursive'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #990000;">unset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'count_recursive'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$recursive</span><span style="color: #339933;">=-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'count'</span><span style="color: #339933;">,</span> 
        <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span>
            <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
                <span style="color: #0000ff;">'conditions'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span>
                <span style="color: #0000ff;">'recursive'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$recursive</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
                <span style="color: #000088;">$extra</span>
            <span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Получилось, на мой взгляд вполне неплохо.</p>
<p><strong>Model::paginate</strong></p>
<p>После доработки подсчета я решил посмотреть, что можно сделать с самим запросом на выборку. По сути, find(&#8216;all&#8217;, &#8230;) он и есть find(&#8216;all&#8217;, &#8230;) чего там еще с ним сделаешь? Тем не менее, из метода контроллера <em>Controller::paginate()</em>, одноименный метод модели <em>Model::paginate()</em> вызывается с множеством параметров.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> paginate<span style="color: #009900;">&#40;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span> <span style="color: #000088;">$fields</span><span style="color: #339933;">,</span> <span style="color: #000088;">$order</span><span style="color: #339933;">,</span> <span style="color: #000088;">$limit</span><span style="color: #339933;">,</span> <span style="color: #000088;">$page</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #000088;">$recursive</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>Для простых запросов тут не стоило бы и напрягаться. Зато для сложных запросов, которые CakePHP не может выполнить сам, или выполняет неоптимально, вызов этого метода &#8212; золотое дно. Ведь здесь можно манипулировать результирующими данными.</p>
<p>В качестве примера, простая ситуация с цепочкой из трех моделей. Таблица с продуктами, у которых есть много цен (розничная, опт1, и т.д.) в разных валютах (их всего три, так получилось). Для работы только с розничными ценами мы перепривязали с помощью <em>unbindModel()</em> и <em>bindModel()</em> модель с ценами как hasOne с условием Price.price_type=&#8217;retail&#8217;. Но цепочка из трех моделей осталась:</p>
<ul>
<li> модель Product владеет ценой (hasOne) Price;</li>
<li>Price принадлежит валюте (belongsTo) Currency</li>
</ul>
<p>Обычный <em>Model::find(&#8216;all&#8217;&#8230;)</em> поступит просто. Выберет все записи Product-JOIN-Price, а потом для каждого элемента массива выберет Currency. С учетом того, что в самом худшем варианте, на странице будут цены в трех валютах, двадцать запросов &#8216;SELECT Price &#8230; &#8216; все равно выглядят удручающе. В похожей ситуации я поступил так:</p>
<ul>
<li>С помощью recursive при вызове Model::find(&#8216;all&#8217;,&#8230;) ограничил выборку только связью Product-Price</li>
<li>Используя Set::classicExtract получил массив id нужных валют. Можно было и с помощью SQL запроса SELECT DISTINCT это сделать, но Set::classicExtract мне ближе ;-)</li>
<li>Выбрал нужные вылюты из модели Currency</li>
<li>Добавил в массив, полученный в первом пункте выбранные валюты в соответствии с Price.currency_id</li>
</ul>
<p>Получилось всего три запроса, включая запрос на подсчет!</p>
<p>Возможность оптимизации менее сложных запросов при вызове <em>Controller::paginate()</em> тоже есть.</p>
<p>Когда я сделал свой метод <em>Model::paginateCount()</em> для общей и админской части, обратил внимание, что возможен вариант, когда в админской части заданы такие условия выборки, под которые не попадает ни одной записи. Например, в указанном разделе автор не опубликовал ни одной записи. В этом случае возвращается, как нетрудно догадаться, пустой массив. Поскольку меня уже было не остановить :-) , я задался вопросом &#8212; а если нет ни одной записи, и <em>paginateCount()</em> это уже определил, то зачем вообще выполнять запрос к БД на выборку? Можно же просто вернуть пустой массив! Так и поступил.</p>
<p>В модель добавил переменную, в которую <em>Model::paginateCount()</em> записывал полученное количество записей.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000088;">$_lastPaginationCountResult</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> paginateCount<span style="color: #009900;">&#40;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span> <span style="color: #000088;">$recursive</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</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: #339933;">@</span><span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'count_recursive'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #990000;">unset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'count_recursive'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$recursive</span><span style="color: #339933;">=-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_lastPaginationCountResult <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'count'</span><span style="color: #339933;">,</span> 
        <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span>
            <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
                <span style="color: #0000ff;">'conditions'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span>
                <span style="color: #0000ff;">'recursive'</span><span style="color: #339933;">=&gt;</span><span style="color: #000088;">$recursive</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
                <span style="color: #000088;">$extra</span>
            <span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_lastPaginationCountResult<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>И написал маленький метод <em>Model::paginate()</em>. Мною собственноручно написана только одна строка, проверка переменной с результатами <em>paginateCount()</em>, которая была вызвана до того. Весь остальной код просто скопировал из соответствующего метода <em>Controller::paginate()</em>.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> paginate<span style="color: #009900;">&#40;</span><span style="color: #000088;">$conditions</span><span style="color: #339933;">,</span> <span style="color: #000088;">$fields</span><span style="color: #339933;">,</span> <span style="color: #000088;">$order</span><span style="color: #339933;">,</span> <span style="color: #000088;">$limit</span><span style="color: #339933;">,</span> <span style="color: #000088;">$page</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #000088;">$recursive</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_lastPaginationCountResult <span style="color: #339933;">===</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$parameters</span> <span style="color: #339933;">=</span> <span style="color: #990000;">compact</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'conditions'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'fields'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'order'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'limit'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'page'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$recursive</span> <span style="color: #339933;">!=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">recursive</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$parameters</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'recursive'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$recursive</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">array_key_exists</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'type'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$type</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'type'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #990000;">unset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$extra</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'type'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">else</span> <span style="color: #000088;">$type</span><span style="color: #339933;">=</span><span style="color: #0000ff;">'all'</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$type</span><span style="color: #339933;">,</span> <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$parameters</span><span style="color: #339933;">,</span> <span style="color: #000088;">$extra</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Кстати, обратите внимание, на параметр type, это не я придумал, это в оригинале так было. Он позволяет вместо find(&#8216;all&#8217;,&#8230;) использовать для выборки любой другой тип запроса. Даже самостоятельно определенный, например, find(&#8216;recent&#8217;,&#8230;), если, конечно, поддержку этого метода кто-нибудь заранее напишет ;-)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/02/improving-pagination-performance-in-cakephp-12/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Методы модели findBy и findAllBy</title>
		<link>http://www.handmadesite.net/2009/02/findby-and-findallby-methods/</link>
		<comments>http://www.handmadesite.net/2009/02/findby-and-findallby-methods/#comments</comments>
		<pubDate>Fri, 13 Feb 2009 11:44:34 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[find]]></category>
		<category><![CDATA[findAllBy]]></category>
		<category><![CDATA[findBy]]></category>
		<category><![CDATA[Models]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[tips and tricks]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=247</guid>
		<description><![CDATA[У методов модели findBy и findAllBy есть насколько параметров, не описанных или лишь слегка упомянутых в документации. Как пользоваться этими методами с максимальной отдачей?]]></description>
			<content:encoded><![CDATA[<p>В документации как-то очень мельком упомянуты эти методы. Кроме того описание этих методов выглядит так:</p>
<p><code>findBy&lt;fieldName&gt;(string $value)<br />
</code></p>
<p>Хотя на самом деле это не совсем соответствует действительности. Для поиска одной записи по значению одного поля описание должно выглядеть так:</p>
<p><code>findBy&lt;fieldName&gt;(string $value, $fields=null, $order=null, $recursive=null)</code></p>
<p>и</p>
<p><code>finAlldBy&lt;fieldName&gt;(string $value, $fields=null, $order=null, $limit=null, $page=null, $recursive=null)</code></p>
<p>Разница есть. Лично я очень обрадовался параметру $recursive &#8212; не надо отдельно, перед вызовом метода устанавливать это значение или ображаться к Containable.</p>
<p>Но и это еще не все!<span id="more-247"></span></p>
<p>Можно задавать несколько полей для условия, объединяя их AND и OR. Вполне работает такая конструкция:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">User</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">findByIdAndActive</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">123</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>Получится запрос типа</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: #66cc66;">*</span> <span style="color: #993333; font-weight: bold;">FROM</span> <span style="color: #993333; font-weight: bold;">USER</span> <span style="color: #993333; font-weight: bold;">WHERE</span> id<span style="color: #66cc66;">=</span><span style="color: #cc66cc;">123</span> <span style="color: #993333; font-weight: bold;">AND</span> active<span style="color: #66cc66;">=</span><span style="color: #cc66cc;">1</span></pre></div></div>

<p>Если в условии участвуют несколько полей, то их значения для поиска должны быть первыми параметрами для вызова метода, в том порядке, в котором перечислены названия полей модели. А уж после значений для поиска можно добавлять аргументы $fields, $order и т.д.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/02/findby-and-findallby-methods/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Добавляем правила проверки данных на лету</title>
		<link>http://www.handmadesite.net/2009/02/adding-validation-rules-on-the-fly/</link>
		<comments>http://www.handmadesite.net/2009/02/adding-validation-rules-on-the-fly/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 16:33:28 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Form]]></category>
		<category><![CDATA[validates]]></category>
		<category><![CDATA[validation]]></category>
		<category><![CDATA[Validator]]></category>
		<category><![CDATA[Модель]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=223</guid>
		<description><![CDATA[Правила проверки, свойство $validates у модели -- самый обычный ассоциативный массив. И никто не запрещает добавлять или удалять элементы из него "по ходу дела". Не забыли?]]></description>
			<content:encoded><![CDATA[<p>Все в общем-то, тривиально и не стоило бы, наверное, пост городить. Но рассматривая код некоторых проектов на CakePHP я обнаружил удивительную вещь &#8212; во многих случаях данные формы проверяются не встроенным валидатором, а специально написанным кодом. Зачем? Непонятно.</p>
<p>Во-первых правила проверки — это просто ассоциативный массив, в который вполне можно добавлять элементы, прямо из метода контроллера. Никуда они при инициализации модели, не парсятся и не обрабатываются, пока не будет вызван валидатор.</p>
<p>Во-вторых валидатор отлично справляется с полями формы, которым нет соответствия в модели. А метод модели save() записывает только те поля, которым соответствуют колонки в таблице.</p>
<p>Вот, например, простая форма регистрации пользователя.<span id="more-223"></span></p>
<p>Форма очень человеколюбивая, и, даже, не побоюсь этого выражения, <em>user-friendly</em>: у нее есть 2 поля с E-mail (второе для проверки), 2 поля с паролем и еще капча. Регистрируйтесь, наздоровье. :-)</p>
<p>В модели User, само собой, есть только колонки <em>&#8216;email&#8217;</em> и <em>&#8216;password&#8217;</em>, без дубликатов, а колонка <em>&#8216;captcha&#8217;</em> отсутствует совсем.</p>
<p>Изначально в классе модели правила проверки полей &#8216;email&#8217; и &#8216;password&#8217; определены вот такие:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">var</span> <span style="color: #000088;">$validate</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #666666; font-style: italic;">/**************** Password **************/</span>
    <span style="color: #0000ff;">'password'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'minlength'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
            <span style="color: #0000ff;">'rule'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'minLength'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'6'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'message'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'Password must be at least 6 characters long.'</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'allowEmpty'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'required'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'last'</span> <span style="color: #339933;">=&gt;</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;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #666666; font-style: italic;">/************* E-mail ************************/</span>
    <span style="color: #0000ff;">'email'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'mailrule'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
            <span style="color: #0000ff;">'rule'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'email'</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: #0000ff;">'message'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'Please supply a valid email address.'</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'allowEmpty'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'required'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'last'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span>
            <span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
        <span style="color: #666666; font-style: italic;">/* This field mus be unique */</span>
        <span style="color: #0000ff;">'must_be_unique'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
            <span style="color: #0000ff;">'rule'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'isUnique'</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'message'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'User with this e-mail address is already registered'</span><span style="color: #339933;">,</span>
            <span style="color: #0000ff;">'last'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span>
        <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Правил для поля captcha, не определено никаких.</p>
<p>В шаблоне вида, форма и поля выводятся с помощью встроенного помощника (helper) Form. Примерно вот так:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">create</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'action'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'register'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">/* ввод username и прочего пропущены */</span>
&nbsp;
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User.email'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User.confirm_email'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User.password'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User.confirm_password'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'type'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'password'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'User.captcha'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'label'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'Что там на картинке?'</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'type'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'text'</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'value'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">''</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$form</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">end</span><span style="color: #009900;">&#40;</span>__<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Пыщь!!111'</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>А в методе, который отвечает за запись данных, я, перед вызовом <em>User-&gt;save()</em>, просто добавляю еще дополнительные правила для email, password и captcha.</p>
<ol>
<li>Данные из поля формы <em>User.email</em> должны быть идентичны данным из поля формы <em>User.confirm_email</em></li>
<li>Данные из поля формы <em>User.password</em> должны быть идентичны данным из поля формы <em>User.confirm_password</em></li>
<li>Данные из поля формы <em>User.captcha</em> должны быть идентичны содержимому переменной сессии <em>Captcha.code</em></li>
<li>Поле <em>User.captcha</em> не может быть пустым</li>
</ol>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">/* Чтоб сравнивать пароли понадобится помощь
 * компонента авторизации
 */</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">User</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">validate</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'password'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'confirm'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'rule'</span><span style="color: #339933;">=&gt;</span>array<span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'equalTo'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span>
            <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Confirm'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'password'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> ?
                <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Auth</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">password</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Confirm'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'password'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
                <span style="color: #0000ff;">''</span><span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'message'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'Пароли не одинаковые'</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">User</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">validate</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'email'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'confirm'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'rule'</span><span style="color: #339933;">=&gt;</span>array<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'equalTo'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span>
        <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Confirm'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'email'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> ?
            <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'Confirm'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'email'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">:</span> 
            <span style="color: #0000ff;">''</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'message'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'E-mail адреса не одинаковые'</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">/* Получаем код капчи из сессии, а если его нет, сравнивать
 * будем с пустой строкой
 */</span>
<span style="color: #000088;">$captcha_code</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Session</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">check</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Captcha.code'</span><span style="color: #009900;">&#41;</span> ?
    <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Session</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">read</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Captcha.code'</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Session</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">del</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Captcha.code'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">/* Дбавляем единственное правило для несуществующего поля */</span>
<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">User</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">validate</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'captcha'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'rule'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'equalTo'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$captcha_code</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'required'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'allowEmpty'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'message'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">&quot;You're a spam bot?&quot;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">/* ну и здесь */</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">User</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">save</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">// и так далее</span></pre></div></div>

<p>Конечно, строчек кода, если и меньше, чем просто в случае сравнения, то ненамного. Зато встроенный валидатор отлично взаимодействует со встроенным помощником (helper) Form &#8212; сообщения об ошибках записываются в переменную validationErrors, выводятся «автомагически» рядом с соответствующими полями формы и т.д.</p>
<p>И уж совсем необязательно все это выписывать именно в контроллере перед вызовом <em>save()</em>. Можно добавить методов модели типа <em>addCaptchaRule()</em>, <em>addEmailConfirmationRule()</em> или типа того. Получится стильно и благородно. ;-)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/02/adding-validation-rules-on-the-fly/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Вечный логин</title>
		<link>http://www.handmadesite.net/2009/02/login-forever/</link>
		<comments>http://www.handmadesite.net/2009/02/login-forever/#comments</comments>
		<pubDate>Tue, 03 Feb 2009 00:37:47 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[AuthComponent]]></category>
		<category><![CDATA[авторизация]]></category>
		<category><![CDATA[Детские грабли]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=216</guid>
		<description><![CDATA[Используя встроенный в CakePHP компонент для аутентификации пользователей, AuthComponent, можно легко обеспечить возможность ввода логина и пароля до морковкиного заговения :-)]]></description>
			<content:encoded><![CDATA[<p>Используя встроенный в <a href="http://cakephp.org/" target="_blank">CakePHP</a> компонент для аутентификации пользователей, <a href="http://book.cakephp.org/view/172/Authentication" target="_blank">AuthComponent</a>, можно легко обеспечить возможность ввода логина и пароля до морковкиного заговения. :-)</p>
<p>Самым нетерпеливым раскрываю суть: запретите доступ неавторизованному пользователю к действию <em>logout</em>. :-)</p>
<p>Схема работы методов компонента простая. Если пользователь неавторизован, то URL, который он запрашивает сохраняется в сессии, а сам пользователь перенаправляется на страницу авторизации. При выходе (вызов метода <em>logout</em>) &#8212; все наоборот: запоминается referer, данные о пользователе убираются из сессии, он становится неавторизованным и перенаправляется обратно, на тот URL, с которого пришел.</p>
<p>Если неавторизованному пользователю запретить доступ к <em>logout</em>, при попытке обращения к этому действию, пользователь будет перенаправлен на страницу входа, наберет логин-пароль и отправится сразу на запрашиваемую им страницу с выходом, там его авторизация закончится и он снова будет отправлен на страницу входа&#8230;</p>
<p><span id="more-216"></span>Надо доступ к <em>logout</em> все-таки всем разрешать. И, на всякий случай, в начале метода написать</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">Auth</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">user</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cakeError</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'error404'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Ибо нефиг искать выход, если еще не вошел :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/02/login-forever/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

