<?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>Mon, 14 Jun 2010 10:27:37 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<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="/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>4</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 не пугался этого расширения и вызывал правильное действие (action), [...]]]></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>0</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> User <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>
		<item>
		<title>SluggableBehavior &#8212; помощник в создании ЧПУ</title>
		<link>http://www.handmadesite.net/2009/01/sluggablebehavior-a-seo-optimizer-friend/</link>
		<comments>http://www.handmadesite.net/2009/01/sluggablebehavior-a-seo-optimizer-friend/#comments</comments>
		<pubDate>Thu, 08 Jan 2009 09:29:02 +0000</pubDate>
		<dc:creator>Сергей</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[behavior]]></category>
		<category><![CDATA[sef]]></category>
		<category><![CDATA[slug]]></category>
		<category><![CDATA[Модели]]></category>
		<category><![CDATA[Модель]]></category>
		<category><![CDATA[чпу]]></category>

		<guid isPermaLink="false">http://www.handmadesite.net/?p=184</guid>
		<description><![CDATA[После выхода стабильной версии CakePHP количество постов в разных блогах, посвященных этому фреймворку, сократилось. Даже в Bakery тишина. Либо Рождество с Новым годом, либо все, засучив рукава, занялись разработкой.
В помощь неутомимым пекарям я решил рассказать об удобном расширении модели (behavior). Вещь, на мой взгляд, полезная. Работает отлично, я этим behavior пользуюсь уже почти год. Он [...]]]></description>
			<content:encoded><![CDATA[<p>После выхода стабильной версии CakePHP количество постов в разных блогах, посвященных этому фреймворку, сократилось. Даже в Bakery тишина. Либо Рождество с Новым годом, либо все, засучив рукава, занялись разработкой.</p>
<p>В помощь неутомимым пекарям я решил рассказать об удобном расширении модели (behavior). Вещь, на мой взгляд, полезная. Работает отлично, я этим behavior пользуюсь уже почти год. Он помогает автоматически, при записи, сгенерировать slug для строки таблицы.</p>
<p>Вот, кстати, мне всегда было интересно, как правильно перевести на русский слово slug в этом контексте. Ярлык?</p>
<p>Этот behavior написал Mariano Iglesias. Это часть его проекта <a href="http://cake-syrup.sourceforge.net/">Cake Syrup</a>. О другой интересной составляющей, <a href="http://com.spweb.ru/archives/181" target="_blank">SoftDeletableBehavior, недавно, кстати, можно прочесть здесь</a>.</p>
<p>Это расширение не использует Inflector::slug() — когда оно создавалось, этого метода еще не было. Зато поддержка транслита русских букв в UTF-8 уже встроена, не без помощи Вашего покорного слуги. ;-)<span id="more-184"></span></p>
<p>Работает расширение очень просто — при сохранении записи выбранное поле, по умолчанию &#8216;title&#8217;, преобразуется в slug и это значение присваивается соответствующему полю БД (по умолчанию &#8217;slug&#8217;). Т.е. заголовок записи &laquo;Моя первая запись&raquo; автомагически превращается в &laquo;moja-pervaja-zapis&raquo;. После этого можно формировать ссылки вида &#8216;/controller/action/my-slug&#8217; а в соответствующем методе контроллера искать запись:</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;">Model</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">findBySlug</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Подключатся behavior, как и все остальные, в классе модели:</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;">$actsAs</span><span style="color: #339933;">=</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'Sluggable'</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: #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;">'Sluggable'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
    <span style="color: #0000ff;">'translation'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'utf-8'</span><span style="color: #339933;">,</span>
    <span style="color: #0000ff;">'label'</span><span style="color: #339933;">=&gt;</span><span style="color: #0000ff;">'myfield'</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>Настойки могут быть такие:</p>
<ul>
<li><strong>label</strong> &mdash; строка или массив. Здесь перечисляются поля, из содержимого которых нужно генерировать slug. Например если указать поля &#8216;author&#8217; и &#8216;book_name&#8217;, то из массива array(&#8216;author&#8217;=>&#8217;Абрамов&#8217;, &#8216;book_name&#8217;=>&#8217;Опознай живого&#8217;) получится slug &#8216;abramov-opoznay-zhivogo&#8217;. Указанные элементы массива не обязательно должны быть столбцами таблицы базы данных. Если такого столбца в таблице нет (например нету author), то не страшно &mdash; надо лишь следующему параметру, <em>real</em>, установить значение false. По-умолчанию параметр label равен &#8216;title&#8217;</li>
<li><strong>real</strong> &mdash; логическое значение, true или false. По-умолчанию true. Как я уже написал, если этот параметр установить в false, то элемент массива, по содержимому которого (или которых) будет создаваться slug, не обязательно должен сответствовать названию столбца в таблице БД.</li>
<li><strong>slug</strong> &mdash; имя столбца БД, в который будет записан результат работы behavior. По-умолчанию &mdash; &#8217;slug&#8217;</li>
<li><strong>separator</strong> &mdash; строка. Это разделитель для слов, на который будут заменяться пробелы. по умолчанию &#8216;-&#8217; (тире).</li>
<li><strong>length</strong> &mdash; целое число, максимальная длина поля slug. По-умолчанию 100 символов.</li>
<li><strong>overwrite</strong> &mdash; логическое значение, по-умолчанию false. Этот параметр позволяет указать, что при обновлении существующей записи в БД slug надо сгенерировать заново и перезаписать.</li>
</ul>
<p>Без доработок дело не обошлось. Дело в том, что behavior для новой записи порывается создать slug в обязательном порядке, а мне хотелось бы чтоб он срабатывал только в случае, если это поле не указано. Иногда вручную получается лучше. Например заголовок &laquo;Мой сайт&raquo; будет преобразован в &laquo;moy-sayt&raquo;, а хотелось бы &laquo;my-site&raquo;. Поэтому я добавил проверку на наличие элемента массива. Теперь если при создании новой записи явно указать slug вручную, ничего автоматически пересоздаваться не будет, только, на всякий случай, slug проверится на уникальность.</p>
<p>Скачать SluggableBehavior можно со <a href="http://cake-syrup.sourceforge.net/download/">страницы проекта CakeSyrup</a>.</p>
<p>А здесь <a href='http://www.handmadesite.net/wp-content/uploads/2009/01/sluggable.patch'>маленький патч</a>, который я описал парой строк выше.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.handmadesite.net/2009/01/sluggablebehavior-a-seo-optimizer-friend/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
