<?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; sprig</title>
	<atom:link href="http://brotkin.ru/tag/sprig/feed/" rel="self" type="application/rss+xml" />
	<link>http://brotkin.ru</link>
	<description>ковыряемся в Internet</description>
	<lastBuildDate>Wed, 02 May 2012 12:49:25 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Ko3: модуль Sprig. Связи</title>
		<link>http://brotkin.ru/2009/12/21/ko3-sprig2-2/</link>
		<comments>http://brotkin.ru/2009/12/21/ko3-sprig2-2/#comments</comments>
		<pubDate>Mon, 21 Dec 2009 20:30:19 +0000</pubDate>
		<dc:creator>biakaveron</dc:creator>
				<category><![CDATA[Kohana3]]></category>
		<category><![CDATA[relations]]></category>
		<category><![CDATA[sprig]]></category>

		<guid isPermaLink="false">http://brotkin.ru/?p=284</guid>
		<description><![CDATA[Итак, продолжаем изучать теорию по использованию модуля Sprig. Напомню, что в предыдущей статье я описал общие правила описания моделей. Но ведь сама по себе модель, отделенная от своих &#8220;соседей&#8221;, не несет большого смысла и практической ценности. Поэтому необходимо рассмотреть возможности Sprig в части работы с взаимосвязями моделей. 
Сообщаем о наличии связей в модели
Как мы помним, [...]]]></description>
			<content:encoded><![CDATA[<p>Итак, продолжаем изучать теорию по использованию модуля <strong>Sprig</strong>. Напомню, что в предыдущей статье я описал общие правила описания моделей. Но ведь сама по себе модель, отделенная от своих &#8220;соседей&#8221;, не несет большого смысла и практической ценности. Поэтому необходимо рассмотреть возможности <strong>Sprig</strong> в части работы с взаимосвязями моделей. <span id="more-284"></span></p>
<h2>Сообщаем о наличии связей в модели</h2>
<p>Как мы <a href="http://brotkin.ru/2009/11/02/ko3-sprig/">помним</a>, в модуле <strong>Sprig</strong> все используемые поля таблицы объявляются в методе <code>_init()</code>, и поля связей тут не исключение. Есть четыре класса для описания различных типов связей (тут все похоже на <a href="http://brotkin.ru/2009/04/18/orm-svyazi/">связи в ORM</a>, так что я сильно их расписывать не буду):</p>
<ul>
<li>Sprig_Field_HasOne</li>
<li>Sprig_Field_HasMany</li>
<li>Sprig_Field_BelongsTo</li>
<li>Sprig_Field_ManyToMany</li>
</ul>
<p>Названия говорят сами за себя. Поскольку в каждой связи участвует две стороны, нет смысла рассматривать классы по отдельности, так что давайте возьмем следующую ситуацию:</p>
<p>1. Есть записи в блоге (модель <code>blog</code>).<br />
2. Есть тэги, которые вешаются на записи блога (модель <code>tag</code>).<br />
3. Есть пользователи (модель <code>user</code>).<br />
4. Есть статистика по пользователям блога. Т.к. она не относится непосредственно к модели пользователя (мы предполагаем, что есть и другие модули, где используется <code>user</code>), выделяем ее в отдельную модель (<code>userinfo</code>).</p>
<p>В упрощенном виде схема получится примерно такая:<br />
<div id="attachment_287" class="wp-caption aligncenter" style="width: 510px"><a href="http://brotkin.ru/wp-content/uploads/sprig_example.png"><img src="http://brotkin.ru/wp-content/uploads/sprig_example.png" alt="Схема таблиц" title="Схема таблиц" width="500" height="471" class="size-full wp-image-287" /></a><p class="wp-caption-text">Схема таблиц</p></div></p>
<p>Тут использованы соглашения по умолчанию для <strong>ORM</strong> (первичный ключ называется `<em>id</em>`, внешние ключи формируются как <code>название модели + суффикс `_id`</code>, и т.д.). Какие модели мы в итоге получим? Давайте я сразу приведу их код, а после разберем типы связей по отдельности.</p>
<p><code></p>

<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: #000000; font-weight: bold;">class</span> Model_User <span style="color: #000000; font-weight: bold;">extends</span> Sprig
<span style="color: #009900;">&#123;</span>
	protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'id'</span>               <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'username'</span>   <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Char<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'email'</span>         <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Email<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'userinfo'</span>      <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_HasOne<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'articles'</span>       <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_HasMany<span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// статьи</span>
<span style="color: #000000; font-weight: bold;">class</span> Model_Article <span style="color: #000000; font-weight: bold;">extends</span> Sprig
<span style="color: #009900;">&#123;</span>
	protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'id'</span>              <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'title'</span>           <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Char<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'text'</span>           <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Text<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'user'</span>          <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'tags'</span>          <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_ManyToMany<span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// тэги</span>
<span style="color: #000000; font-weight: bold;">class</span> Model_Tag <span style="color: #000000; font-weight: bold;">extends</span> Sprig
<span style="color: #009900;">&#123;</span>
	protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'id'</span>            <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'name'</span>      <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Char<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'articles'</span>    <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_ManyToMany<span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// доп. информация о пользователях</span>
<span style="color: #000000; font-weight: bold;">class</span> Model_Userinfo <span style="color: #000000; font-weight: bold;">extends</span> Sprig
<span style="color: #009900;">&#123;</span>
	protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'id'</span>       <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'data'</span>   <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Text<span style="color: #339933;">,</span>
			<span style="color: #0000ff;">'user'</span>   <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code></p>
<blockquote><p>Условно все поля можно разбить на две категории &#8211; реальные поля и псевдополя. Примеры реальных полей &#8211; `<em>id</em>`, `<em>email</em>` и `<em>username</em>` в модели <strong>Model_User</strong>, они присутствуют в таблице. Псевдополя являются результатом наличия связей с другими моделями. Например, поля `<em>articles</em>` и `<em>userinfo</em>`, которые не могут быть сохранены в БД в явном виде. Для них свойство <code>$in_db</code> установлено в <strong>FALSE</strong>. Не стоит путать внешний ключ (например, `<em>user_id</em>` в таблице `<em>articles</em>`) с псевдополем `<em>user</em>` &#8211; в первом случае мы просто работаем с численным значением, второй вариант дает возможность получить связанный объект. </p></blockquote>
<h2>HasOne</h2>
<p>Примером данной связи будет наличие доп. информации у пользователя. В модели <strong>Model_User</strong> объявлено псевдополе <code>$userinfo</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'userinfo'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_HasOne<span style="color: #339933;">,</span></pre></div></div>

<p>Так как никаких дополнительных параметров при создании псевдополя не передавалось, то при обращении к нему будут автоматически вычислены следующие свойства связи:</p>
<ul>
<li>Имя связанной модели `<em>model</em>` будет `<em>userinfo</em>` (по названию псевдополя).</li>
<li>Имя внешнего ключа в той модели (`<em>column</em>`) определится как `<em>user_id</em>` (<code>имя текущей модели + '_id'</code>).</li>
</ul>
<blockquote><p>
Схема определения имени внешнего ключа не является жестко фиксированной &#8211; используется метод <code>fk($table = NULL)</code> модели. Вы можете его переопределить, чтобы, к примеру, возвращать &#8216;<em>user_key</em>&#8216;. Кроме того, если в Вашей модели используется нестандартный первичный ключ (например &#8216;<em>key</em>&#8216;), то по умолчанию метод <code>pk()</code> будет возвращать что-то вроде &#8216;<em>user_key</em>&#8216; вместо &#8216;<em>user_id</em>&#8216; &#8211; будьте внимательны!</p></blockquote>
<p>Теоретически, теперь мы можем попробовать обратиться к свойству <code>$user->userinfo</code>:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$user</span> <span style="color: #339933;">=</span> Sprig<span style="color: #339933;">::</span><span style="color: #004000;">factory</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;">'id'</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: #b1b100;">echo</span> <span style="color: #000088;">$user</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">userinfo</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">data</span><span style="color: #339933;">;</span></pre></div></div>

<p></code></p>
<p>К сожалению, результата не будет. Что-то не так? Методом проб и ошибок определяем, что метод load() не возвращает ожидаемое (а мы предполагаем модель <strong>Userinfo</strong>, где поле &#8216;<em>user_id</em>&#8216; равно 1). А проблема вот в чем &#8211; надо объявить данную связь на другой ее стороне, т.е. собственно в модели <strong>Userinfo</strong>. Это связь типа <strong>BelongsTo</strong>, так что имеет смысл перейти к ней.</p>
<h2>BelongsTo</h2>
<p>Через данный тип связи объявляем, что каждый объект <strong>Userinfo</strong> привязан к родительскому объекту <strong>User</strong>, в нашем случае через поле &#8216;<em>user_id</em>&#8216;. Так как пока что все свойства рассматриваемых моделей (ключи, имена связей и т.д.) максимально простые, то и объявление будет коротким:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'user'</span>   <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>Автоматически заполняются свойства связи:</p>
<ul>
<li>&#8216;<em>model</em>&#8216; &#8211; имя связанной модели, в данном случае &#8216;<em>user</em>&#8216;.</li>
<li>&#8216;<em>column</em>&#8216; &#8211; имя поля внешнего ключа, получаем через метод <code>fk()</code> модели <strong>Model_User</strong> (по умолчанию вернет &#8216;<em>user_id</em>&#8216;).</li>
</ul>
<p>Поскольку данный тип связи используется не только для <strong>HasOne</strong>, но и с <strong>HasMany</strong>, аналогичное объявление мы сделали в модели <strong>Model_Article</strong> для связи с <strong>Model_User</strong>:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'user'</span>   <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>Теперь можно вызывать <code>$userinfo->user->load()->username</code>, причем обратную сторону связи в данном случае прописывать необязательно. Таким образом, делаем вполне логичный вывод &#8211; <strong>связь обязательно должна быть прописана в пассивной модели (т.е. в модели, в которую переходит внешний ключ)</strong>.</p>
<h2>HasMany</h2>
<p>Собственно тут тоже ничего сложного, вот так в <strong>Model_User</strong> объявлено, что каждый пользователь может понаписать множество статей в блоге:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'articles'</span>       <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_HasMany<span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>&#8220;На автопилоте&#8221; определяется свойство &#8216;<em>model</em>&#8216; &#8211; имя модели, получаем как название связи в единственном числе (через <code>Inflector::singular()</code>) &#8211; &#8216;<em>article</em>&#8216;. Свойство &#8216;<em>column</em>&#8216; бессмысленно, т.к. в результате данной связи в таблице никаких полей не добавляется.</p>
<h2>ManyToMany</h2>
<p>Тут тоже все просто и скучно, в модели <strong>Model_Article</strong> объявляем о тэгах:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'tags'</span>          <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_ManyToMany<span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<ul>
<li>Свойство &#8216;<em>model</em>&#8216; получится как &#8216;<em>tag</em>&#8216; (имя связи в единственном числе).</li>
<li>Свойство &#8216;<em>through</em>&#8216; (имя промежуточной таблицы) вычисляется аналогично <strong>ORM</strong>: берем имена таблиц (в данном случае это &#8216;<em>articles</em>&#8216; и &#8216;<em>tags</em>&#8216;), ставим по алфавиту и разделяем знаком подчеркивания, т.е. &#8216;<em>articles_tags</em>&#8216; в итоге.</li>
<li>Свойство &#8216;<em>column</em>&#8216; не заполняется ввиду неактуальности. </li>
</ul>
<blockquote><p>Что любопытно &#8211; после вычисления свойства &#8216;<em>through</em>&#8216; для пары статья-тэг становится доступным статическое свойство <code>Sprig::$_relations['article_tag']</code>, которое содержит имя промежуточной таблицы. Таким образом, если из модели <strong>Model_Tag</strong> обратиться к свойству <code>articles</code>, промежуточная таблица будет взята оттуда. Теоретически это должно позволить менять одну из главных настроек связи <strong>ManyToMany</strong> только в одном месте, но у меня это не вызвало особого интереса. Наверное проще указать &#8216;<em>through</em>&#8216; вручную в двух моделях, зато это будет предсказуемо.
</p></blockquote>
<h2>Ленивая загрузка</h2>
<p>Наверняка вы обратили внимание на метод <code>load()</code>, который иногда используется после обращения к связанным объектам. Очевидно, что данный метод вызывает загрузку записей, т.е. непосредственно <code>$user->userinfo</code>, к примеру, вернет только экземпляр <strong>Database_Query_Builder_Select</strong> с примененными параметрами выборки (с условием <code>where('user_id', '=', 1)</code>). В общем, <em>lazy loading</em> к вашим услугам! Правда, это актуально только для <strong>HasOne</strong> и <strong>BelongsTo</strong>.</p>
<p>Идем дальше. Наверняка вы помните, как осуществляется выборка связанных объектов в <strong>ORM</strong>. К примеру, <code>$user->articles->where('approved', '=', 1)->find_all()</code> позволяет выбрать только утвержденные статьи пользователя. Однако в <strong>Sprig</strong> не все так просто. К сожалению, непосредственный вызов <code>$user->articles</code> приведет к выборке всех статей пользователя, метод <code>where()</code> здесь просто некуда &#8220;воткнуть&#8221;. Единственное, что предлагается нам &#8211; все тот же метод <code>load()</code>. В результате то, что в <strong>ORM</strong> получается достаточно элегантно, в <strong>Sprig</strong> выглядит несколько неуклюже:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$query</span> <span style="color: #339933;">=</span> DB<span style="color: #339933;">::</span><span style="color: #004000;">select</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">where</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'user_id'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'='</span><span style="color: #339933;">,</span> <span style="color: #000088;">$user</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">pk</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">where</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'approved'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'='</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$articles</span> <span style="color: #339933;">=</span> Sprig<span style="color: #339933;">::</span><span style="color: #004000;">factory</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'article'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$query</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p></code><br />
Сперва подготовили условия (авторство и наличие флага &#8220;утверждено&#8221;), потом подставили в метод <code>load()</code>. Не забудьте про второй параметр, т.к. по умолчанию количество записей ограничивается одной. Впрочем, можно установить нужный лимит записей, например для разбивки на страницы (т.е. для пагинатора).</p>
<blockquote><p>
Это актуально и для безусловных выборок. Например, получаем всех пользователей:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$users</span> <span style="color: #339933;">=</span> Sprig<span style="color: #339933;">::</span><span style="color: #004000;">factory</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'user'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p></code></p></blockquote>
<p>Да, это работает. Но теряется вся красота и прозрачность, которая сопутствует <strong>ORM</strong>. Надеюсь, это будет исправлено в следующих коммитах.</p>
<h2>Усложняем задачу</h2>
<p>Конечно, далеко не всегда получается использовать именования таблиц, полей и т.д., которые по умолчанию подходят под соглашения, будь то <strong>ORM</strong> или <strong>Sprig</strong>. Поэтому давайте изменим рассматриваемую схему, которая во второй редакции будет выглядеть вот так:<br />
<a href="http://brotkin.ru/wp-content/uploads/sprig_modern_example.png"><img src="http://brotkin.ru/wp-content/uploads/sprig_modern_example.png" alt="" title="усложненная схема" width="500" height="471" class="aligncenter size-full wp-image-290" /></a></p>
<p>Список изменений следующий.</p>
<ul>
<li>Таблица <em>articles_tags</em> переименована в <em>arttags</em>.</li>
<li>Первичный ключ в <em>articles</em> теперь называется <em>articleid</em>.</li>
<li>Внешний ключ <em>user_id</em> переименован в <em>author_id</em>.</li>
</ul>
<p>Теперь посмотрим, как изменятся коды моделей. Для начала разберемся с переименованием первичного ключа. Обычно (и в <strong>ORM</strong> мы используем такой подход) для этого есть специальное свойство <code>$_primary_key</code>. Но одна из особенностей <strong>Sprig</strong> &#8211; некоторые типы полей могут быть заранее обозначены как часть первичного ключа (их свойство <code>$primary</code> установлено в <strong>TRUE</strong>). Это действительно для поля <strong>Sprig_Field_Auto</strong>, поэтому поле `<em>articleid</em>` будет добавлено в свойство <code>$_primary_key</code> автоматически. Другое дело, что в методе <code>_init()</code>, где это поле объявляется, необходимо указать актуальное имя:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
		<span style="color: #0000ff;">'articleid'</span>			<span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
		<span style="color: #339933;">...</span></pre></div></div>

<p></code></p>
<blockquote><p>Стоит обратить внимание, что текущая версия <strong>Sprig</strong> не осуществляет проверку при добавлении поля в список первичного ключа, так что в случае прописывания свойства <code>$_primary_key</code> вручную скорее всего одно и то же поле будет добавлено туда дважды.</p></blockquote>
<p>Идем дальше. Для связи с пользователем модель <strong>Article</strong> теперь использует внешний ключ `<em>author_id</em>`. В связи с этим меняем объявление поля в методе <code>_init()</code>:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'user'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'column'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'author_id'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>Как мы видим, чтобы переопределить дефолтные названия для внешних ключей, надо использовать параметр <code>column</code>. Сделаем еще одно изменение &#8211; пусть доступ к модели <strong>User</strong> будет осуществляться по псевдополю &#8216;<em>author</em>&#8216; (т.е. <code>$article->author</code> вместо <code>$article->user</code>, что более логично). Для этого надо переименовать имя поля, а также явно указать название связанной модели, которое ранее вычислялось автоматически:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'author'</span>	<span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_BelongsTo<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'model'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'user'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'column'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'author_id'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>Ну и напоследок &#8211; указываем в явном виде новое имя промежуточной модели для связи <strong>ManyToMany</strong>:<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #0000ff;">'tags'</span><span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_ManyToMany<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'through'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'arttags'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span></pre></div></div>

<p></code></p>
<p>Прописали? Проверяем, работает?! Ан нет, не работает. Вспомните алгоритм вычисления наименования внешнего ключа &#8211; по умолчанию все зависит от первичого ключа. Поэтому <strong>JOIN</strong> таблиц <em>arttags</em> и <em>tags</em> будет осуществляться по некорректному полю &#8216;<em>article_articleid</em>&#8216;. Как этого избежать, я уже упоминал &#8211; надо переопределить метод <code>fk()</code>, чем нам и придется заняться:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> fk<span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #000088;">$key</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'article_id'</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</span><span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$table</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;">&#123;</span>
			<span style="color: #000088;">$table</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_table<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #000088;">$table</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'.'</span><span style="color: #339933;">.</span><span style="color: #000088;">$key</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> <span style="color: #000088;">$key</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code><br />
Конечно, не самое красивое решение. Во первых, приходится копировать исходный метод полностью, а хотелось бы в полной мере попользоваться наследованием, дабы в своем методе (условно говоря) вычислить переменную <code>$key</code> (или сохранить ее в каком-нибудь свойстве с говорящим названием <code>$_foreign_key</code>) и далее вызвать <code>parent::fk()</code>. Кроме того, вдруг в какой-нибудь другой таблице внешний ключ будет переходить под другим названием? Единственное приходящее мне в данный момент решение &#8211; добавление <code>switch ... case</code> с перечислением известных вариантов значений для переменной <code>$table</code> (по крайней мере для <strong>ManyToMany</strong> метод <code>fk()</code> всегда использует данный параметр). Вспоминается поговорка про мышей и кактус <img src='http://brotkin.ru/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h2>На этом все</h2>
<p>Согласитесь, налицо существенные недостатки и никаких преимуществ перед <strong>ORM</strong>. Так почему я пишу о данном модуле, а не придумываю новый повод написать очередную статью про <strong>ORM</strong>? Намекну, что главная сила <strong>Sprig</strong> не в связях. <strong>Sprig</strong> удобен тем, что мы унифицируем работу с полями модели/таблицы &#8211; генерацию и обработку форм в первую очередь. Но это уже тема для следующей &#8211; итоговой &#8211; статьи, в которой я покажу, что нам собственно делать с предоставляемыми возможностями.</p>

<div class="bookmarkz"><a href="http://www.google.com/bookmarks/mark?op=add&bkmk=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/google.png" border="0" width="16" height="16" alt="Google Bookmarks" title="Google Bookmarks"></a> <a href="http://digg.com/submit?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/digg.png" border="0" width="16" height="16" alt="Digg" title="Digg"></a> <a href="http://reddit.com/submit?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/reddit.png" border="0" width="16" height="16" alt="Reddit" title="Reddit"></a> <a href="http://del.icio.us/post?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/delicious.png" border="0" width="16" height="16" alt="del.icio.us" title="del.icio.us"></a> <a href="http://ma.gnolia.com/beta/bookmarklet/add?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web&description=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/magnolia.png" border="0" width="16" height="16" alt="Ma.gnolia" title="Ma.gnolia"></a> <a href="http://www.technorati.com/faves?add=http://brotkin.ru/2009/12/21/ko3-sprig2-2/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/technorati.png" border="0" width="16" height="16" alt="Technorati" title="Technorati"></a> <a href="http://www.slashdot.org/bookmark.pl?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/slashdot.png" border="0" width="16" height="16" alt="Slashdot" title="Slashdot"></a> <a href="http://myweb2.search.yahoo.com/myresults/bookmarklet?u=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&t=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/yahoo.png" border="0" width="16" height="16" alt="Yahoo My Web" title="Yahoo My Web"></a> <a href="http://news2.ru/add_story.php?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/news2ru.png" border="0" width="16" height="16" alt="News2.ru" title="News2.ru"></a> <a href="http://www.bobrdobr.ru/addext.html?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/bobrdobr.png" border="0" width="16" height="16" alt="БобрДобр.ru" title="БобрДобр.ru"></a> <a href="http://rumarkz.ru/bookmarks/?action=add&popup=1&address=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/rumarkz.png" border="0" width="16" height="16" alt="RUmarkz" title="RUmarkz"></a> <a href="http://www.vaau.ru/submit/?action=step2&url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/vaau.png" border="0" width="16" height="16" alt="Ваау!" title="Ваау!"></a> <a href="http://memori.ru/link/?sm=1&u_data[url]=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&u_data[name]=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/memori.png" border="0" width="16" height="16" alt="Memori.ru" title="Memori.ru"></a> <a href="http://www.rucity.com/bookmarks.php?action=add&address=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/rucity.png" border="0" width="16" height="16" alt="rucity.com" title="rucity.com"></a> <a href="http://moemesto.ru/post.php?url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/moemesto.png" border="0" width="16" height="16" alt="МоёМесто.ru" title="МоёМесто.ru"></a> <a href="http://www.mister-wong.ru/index.php?action=addurl&bm_url=http://brotkin.ru/2009/12/21/ko3-sprig2-2/&bm_description=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%A1%D0%B2%D1%8F%D0%B7%D0%B8+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/mrwong.png" border="0" width="16" height="16" alt="Mister Wong" title="Mister Wong"></a> </div>
]]></content:encoded>
			<wfw:commentRss>http://brotkin.ru/2009/12/21/ko3-sprig2-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ko3: модуль Sprig. Начало работы.</title>
		<link>http://brotkin.ru/2009/11/02/ko3-sprig/</link>
		<comments>http://brotkin.ru/2009/11/02/ko3-sprig/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 08:33:01 +0000</pubDate>
		<dc:creator>biakaveron</dc:creator>
				<category><![CDATA[Kohana3]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[sprig]]></category>

		<guid isPermaLink="false">http://brotkin.ru/?p=282</guid>
		<description><![CDATA[Библиотека ORM всегда вызывала множество вопросов как у новичков, так и у более опытных пользователей Kohana. Удобная в использовании, но сложная в изучении, она проскакивает наверное в каждом третьем-четвертом топике официального форума. А знаете ли Вы, что есть и другие библиотеки? Для 2.3.4 есть Auto Modeler от zombor (Jeremy Bush) и его переработанная версия &#8211; [...]]]></description>
			<content:encoded><![CDATA[<p>Библиотека <strong>ORM</strong> всегда вызывала множество вопросов как у новичков, так и у более опытных пользователей <strong>Kohana</strong>. Удобная в использовании, но сложная в изучении, она проскакивает наверное в каждом третьем-четвертом топике официального форума. А знаете ли Вы, что есть и другие библиотеки? Для <em>2.3.4</em> есть <a href="http://dev.kohanaphp.com/projects/automodeler">Auto Modeler</a> от <strong>zombor</strong> (<strong>Jeremy Bush</strong>) и его переработанная версия &#8211; <a href="http://dev.kohanaphp.com/projects/simple-modeler">Simple Modeler</a> от <strong>thejw23</strong>. А поскольку в последнее время мы больше обращаем внимание на <strong>Ko3</strong>, рассмотрим альтернативу <strong>ORM</strong>, созданную самим создателем фреймворка &#8211; <a href="http://github.com/shadowhand/sprig/">Sprig</a> от <strong>Shadowhand</strong> (<strong>Woody Gilk</strong>).</p>
<p>Как мы знаем, основная &#8220;фишка&#8221; <strong>ORM</strong> &#8211; прозрачная работа с таблицами БД. Т.е. мы можем не описывать имеющиеся в таблице поля, а обращаться к ним как к свойствам модели. Это становится возможным благодаря автоматическому запросу (<em>SHOW COLUMNS</em>, если говорить о <strong>MySQL</strong>) к таблице при создании модели, загружающему информацию о структуре таблицы. Впрочем, это не освобождает от необходимости объявлять связи с другими таблицами.</p>
<blockquote><p>Возможно Вы не знаете &#8211; когда Ваша таблица примет окончательные очертания, можно прописать мета-данные о формате полей в статическом свойстве <code>$_column_cache</code> (это массив, используем ключ <code>$this->_object_name</code>), чтобы <strong>ORM</strong> не запрашивал их каждый раз. Проще всего &#8220;подсмотреть&#8221; эти данные (используя <code>var_dump()</code> или <code>Kohana::dump()</code>) и скопировать в <code>$_column_cache</code>.</p></blockquote>
<p>А что же <strong>Sprig</strong>? Тут немного другая философия &#8211; каждое поле таблицы представлено отдельным объектом, потомком абстрактного класса <strong>Sprig_Field</strong>. Казалось бы, что тут выдумывать, поле оно и есть поле, делаем его строковым и не волнуемся ни о чем. Ан нет! Использование отдельных классов имеет ряд преимуществ:</p>
<ul>
<li>Многие типы полей имеют сложившиеся форматы вывода на экран (к примеру, дату обычно не выводят в формате <em>UNIX timestamp</em>). С помощью <strong>Sprig</strong> мы можем настроить форматирование любого используемого типа данных.</li>
<li>Аналогично и с генерацией элементов форм. Никто не будет показывать поле логического типа (т.е. <strong>TRUE</strong>/<strong>FALSE</strong>) в виде текстового &#8211; тут нужен <strong>checkbox</strong>. <strong>Sprig</strong> позволяет описать параметры генерируемого поля, в том числе и перечень допустимых значений (для формирования выпадающего списка).</li>
<li>Есть типовые ограничения (уникальность, <em>NOT NULL</em> и т.д.), которые приходится прописывать для каждого поля в свойствах валидации (<code>$_filters</code>, <code>$_rules</code> и <code>$_callbacks</code>). А в <strong>Sprig</strong> поля имеют дополнительные свойства, описывающие различные аспекты хранения их в БД (и они автоматом используются при валидации). Об этом я напишу чуть позже.</li>
</ul>
<h2>Из чего строится модель</h2>
<p>Каждая модель должна быть потомком класса <strong>Sprig</strong> (кто бы сомневался) и реализовывать метод <code>_init()</code>, в котором собственно и описываются все поля таблицы. Например, вот что происходит в модели <strong>Sprig_Model_User</strong> (включена в дистрибутив <strong>Sprig</strong>):<br />
<code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">protected <span style="color: #000000; font-weight: bold;">function</span> _init<span style="color: #009900;">&#40;</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>_fields <span style="color: #339933;">+=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
		<span style="color: #0000ff;">'id'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Auto<span style="color: #339933;">,</span>
		<span style="color: #0000ff;">'username'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Char<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'empty'</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;">'unique'</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;">'rules'</span>  <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
				<span style="color: #0000ff;">'regex'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/^[\pL_.-]+$/ui'</span><span style="color: #009900;">&#41;</span>
			<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		<span style="color: #0000ff;">'password'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Password<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'empty'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		<span style="color: #0000ff;">'password_confirm'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Password<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'empty'</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;">'in_db'</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;">'rules'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
				<span style="color: #0000ff;">'matches'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'password'</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: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		<span style="color: #0000ff;">'last_login'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Timestamp<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'empty'</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;">'editable'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #339933;">,</span>
		<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code></p>
<p>Поля хранятся в свойстве <code>$_fields</code>. Каждый экземпляр класса <strong>Sprig_Field</strong> может быть создан с передачей дополнительных параметров, так что на стандартные для выбранного типа настройки можно сверху &#8220;накатить&#8221; свои. Так, если автоинкрементное поле `<em>id</em>` создается с настройками по умолчанию (что уж там еще настраивать), то для поля `<em>username</em>` добавлено правило валидации (ключ &#8216;<em>rules</em>&#8216;) и свойства поля из БД &#8211; <em>NOT NULL</em> (ключ &#8216;<em>empty</em>&#8216;) и <em>UNIQUE</em> (ключ &#8216;<em>unique</em>&#8216;). Давайте рассмотрим полный список свойств класса <strong>Sprig_Field</strong>.</p>
<ul>
<li><strong>$empty</strong> &#8211; поддерживает ли поле отсутствие значения. <em>NOT NULL</em> по сути. По умолчанию <strong>FALSE</strong>.</li>
<li><strong>$primary</strong> &#8211; является ли первичным ключем (<em>PRIMARY KEY</em>). По умолчанию <strong>FALSE</strong>.</li>
<li><strong>$unique</strong> &#8211; флаг уникальности (<em>UNIQUE KEY</em>). По умолчанию <strong>FALSE</strong>.</li>
<li><strong>$null</strong> &#8211; все пустые значения (например, пустые строки) автоматически будут преобразованы в <strong>NULL</strong>. По умолчанию FALSE.</li>
<li><strong>$editable</strong> &#8211; разрешено ли изменение даного поля пользователем. Может использоваться для автоинкрементых полей и автоматически генерируемых меток времени. По умолчанию <strong>TRUE</strong>.</li>
<li><strong>$default</strong> &#8211; значение по умолчанию.</li>
<li><strong>$choices</strong> &#8211; массив допустимых значений (что-то вроде эмуляции <em>ENUM</em>).</li>
<li><strong>$column</strong> &#8211; имя поля в БД. По умолчанию сгенерируется на основе имени объекта. Вы можете указать более удобный псевдоним для поля в методе <code>_init()</code>, а реальное имя указать в данном свойстве. Обычно это используется для внешних ключей.</li>
<li><strong>$description</strong> &#8211; описание поля.</li>
<li><strong>$in_db</strong> &#8211; существует ли поле в БД (такие поля в <strong>ORM</strong> помещались в <code>$ignored_columns</code>). По умолчанию <strong>TRUE</strong>.</li>
<li><strong>$label</strong> &#8211; метка поля. Используется для формирования элемента <strong>label</strong> формы, а также при валидации. По умолчанию сгенерируется автоматически, используя метод <code>Inflector::humanize()</code>, который заменяет тире и знаки подчеркивания на пробелы.</li>
<li><strong>$filters</strong> &#8211; массив применяемых фильтров валидации.</li>
<li><strong>$rules</strong> &#8211; массив правил для поля.</li>
<li><strong>$callbacks</strong> &#8211; массив внешних функций (коллбэков).</li>
</ul>
<blockquote><p>Немного в стороне стоят свойства <code>$min_length</code> и <code>$max_length</code>, которые могут быть добавлены в текстовые поля для ограничения длины.
</p></blockquote>
<p>Часто используемые комбинации данных свойств представлены в виде готовых классов-потомков <strong>Sprig_Field</strong>.</p>
<ul>
<li><strong>Sprig_Field_Auto</strong> &#8211; автоинкремент. Первичный ключ, нередактируемый, пустые значения преобразовываются в <strong>NULL</strong>.</li>
<li><strong>Sprig_Field_Boolean</strong> &#8211; логическое поле. Может хранить <strong>NULL</strong>, значение по умолчанию &#8211; <strong>FALSE</strong>. При валидации автоматически применяется фильтр <code>filter_var(FILTER_VALIDATE_BOOLEAN)</code>.</li>
<li><strong>Sprig_Field_Char</strong> &#8211; символьное поле. Можно указать минимальную/максимальную длину.</li>
<li><strong>Sprig_Field_Country</strong> &#8211; поле для кода стран в двухсимвольном (за небольшими исключениями) международном формате. Массив <em>код</em> => <em>расшифровка</em> хранится в свойстве <code>$codes</code>, которое будет скопировано в <code>$choices</code> по умолчанию. Если необходимо предоставить возможность ввода других кодов, установите свойство <code>$prompt</code> в <strong>TRUE</strong>.</li>
<li><strong>Sprig_Field_Email</strong> &#8211; поле для электронной почты, добавлено правило <code>validate::email()</code>.</li>
<li><strong>Sprig_Field_Enum</strong> &#8211; поле, имеющее ограниченное число значений. Если Вы забудете их установить в свойстве <code>$choices</code>, получите <strong>Exception</strong>.</li>
<li><strong>Sprig_Field_Float</strong> &#8211; число с плавающей запятой. Можно настроить количество точек после запятой (свойство <code>$places</code>). </li>
<li><strong>Sprig_Field_Image</strong> &#8211; поле для хранения пути к изображению. При создании необходимо явно указать параметр &#8216;<em>path</em>&#8216; &#8211; путь к директории с картинками. Также можно передать длину и ширину (&#8216;<em>width</em>&#8216; и &#8216;<em>height</em>&#8216;).</li>
<li><strong>Sprig_Field_Integer</strong> &#8211; целочисленное значение. Можно задать &#8216;<em>min_value</em>&#8216; и &#8216;<em>max_value</em>&#8216;.</li>
<li><strong>Sprig_Field_Password</strong> &#8211; поле для хранени паролей. Можно указать метод хэширования в свойстве &#8216;<em>hash_with</em>&#8216; (по умолчанию &#8216;<em>sha1</em>&#8216;).</li>
<li><strong>Sprig_Field_Text</strong> &#8211; поле для многострочных текстовых полей.</li>
<li><strong>Sprig_Field_Timestamp</strong> &#8211; поле для хранения даты/времени. Поддерживаются свойства &#8216;<em>auto_now_create</em>&#8216; и &#8216;<em>auto_now_update</em>&#8216; (автоматическая подстановка значения <code>NOW()</code> при создании и редактировании соответственно, по умолчанию <strong>FALSE</strong>) и &#8216;<em>format</em>&#8216; (формат вывода данных на экран через функцию <code>date()</code>, по умолчанию &#8216;<em>Y-m-d G:i:s A</em>&#8216;).</li>
<li><strong>Sprig_Field_ForeignKey</strong> &#8211; абстрактный класс, базовый для внешних ключей. <strong>NOT_NULL</strong>, но в то же время по умолчанию он не является существующим полем в БД (т.е. <code>$in_db == FALSE</code>). Дело в том, что не для всех типов связей внешний ключ располагается в текущей таблице (ниже рассмотрены доступные классы по типам связей). </li>
<li><strong>Sprig_Field_HasOne</strong> &#8211; класс для описания связи <strong>has_one</strong> (один-к-одному). Поле игнорируемое (<code>$in_db == FALSE</code>) и нередактируемое. Этот класс следует применять для &#8220;старшей&#8221; таблицы в связи, т.к. в той, которая экспортирует свой первичный ключ в связанную таблицу. </li>
<li><strong>Sprig_Field_BelongsTo</strong> &#8211; класс для <strong>belongs_to</strong> (обратная сторона связи <strong>has_one</strong> или <strong>has_many</strong>). В данном случае внешний ключ находится в таблице, так что $in_db == TRUE.</li>
<li><strong>Sprig_Field_HasMany</strong> &#8211; класс для <strong>has_many</strong>. Недоступен для редактирования.</li>
<li><strong>Sprig_Field_ManyToMany</strong> &#8211; поле <strong>HABTM</strong>. Может быть отредактировано, имеется дополнительное свойство $through (как Вы наверное догадываетесь, оно предназначено для имени промежуточной таблицы и по умолчанию формируется из названий связанных таблиц по тому же принципу, что и в <strong>ORM</strong>).</li>
</ul>
<p>Кроме собственно полей, есть свойства для хранения мета-данных модели. По большей части они знакомы нам благодаря <strong>ORM</strong>:</p>
<ul>
<li><strong>$_primary_key</strong> &#8211; имя поля для первичного ключа. Теоретически может быть массивом (в случае составного первичного ключа), но вроде как это еще не полностью поддерживаемая возможность.</li>
<li><strong>$_table</strong> &#8211; имя используемой таблицы.</li>
<li><strong>$_title_key</strong> &#8211; аналог <code>$_primary_val</code> из <strong>ORM</strong>. По умолчанию &#8216;name&#8217;.</li>
<li><strong>$_sorting</strong> &#8211; настройка сортировки по умолчанию. Синтаксис <code>имя поля => направление</code>, например <code>array('created' => 'DESC')</code>.</li>
</ul>
<h2>Создание экземпляра модели и работа с ним</h2>
<p>В отличие от <strong>ORM</strong> в <strong>Sprig</strong> конструктор недоступен для прямого вызова, создавать модели необходимо через статический метод <code>factory($name, array $values = NULL)</code>. Это позволяет не создавать модель каждый раз заново, а использовать уже имеющуюся копию данного класса (эта копия представляет собой проинициализированную модель без каких-либо данных, т.е. происходит экономия на времени работы конструктора, в частности на методе <code>_init()</code>). При этом копии между собой не связаны, так что изменения в одной не повлекут корректировку другой.</p>
<p>Второй параметр <code>$values</code> позволяет передать значения для загрузки в модель. Значения, отсутствующие в списке полей таблицы, будут отброшены. Например, то, что в <strong>ORM</strong> записывалось как <code>ORM::factory('blog', 1)</code>, в <strong>Sprig</strong> будет выглядеть как <code>Sprig::factory('blog', array('id' => 1)</code>. Обратите внимание, что идентификатор будет загружен в модель, но запроса к БД не будет. Налицо все та же &#8220;ленивая загрузка&#8221;. Для явного обращения к БД надо использовать метод load().</p>
<p>Можно присваивать значения полям модели вручную, обращаясь к ним как в <strong>ORM</strong>, например <code>$blog->id = 1</code>. Также имеется метод <code>values(array $values)</code> для изменения сразу нескольких полей.</p>
<blockquote><p>$blog->id вернет значение поля. Если необходим сам объект (в данном случае это Sprig_Field_Auto), используйте методы field($name) или fields().</p></blockquote>
<h2>CRUD</h2>
<p>Метод <code>load()</code> я уже упомянул выше. Однако у него есть параметры, которые могут быть полезными для выборки данных из БД. Так, первым параметром идет объект класса <strong>Database_Query_Builder_Select</strong>, т.е. объект запроса на выборку, создаваемый обычно методом <code>DB::select()</code>. С его помощью можно указать дополнительные условия выборки. Второй параметр &#8211; <code>$limit</code>, позволяет ограничить количество отбираемых записей (по умолчанию 1).</p>
<p>Операции создания/редактирования и удаления в <strong>Sprig</strong> представлены отдельными методами (как мы помним, в <strong>ORM</strong> создание и редактирование были объединены в рамках метода <code>save()</code>).</p>
<ul>
<li>Метод <code>create()</code> создает новую запись в таблице, используя текущие значения. Если есть поле (или поля) типа <strong>Sprig_Field_Timestamp</strong> со свойством <code>$auto_now_create == TRUE</code>, то для него значением текущая метка времени. Далее автоматически будет вызвана валидация данных, так что вызовы метода <code>create()</code>, а также (забегаю вперед) и метода <code>update()</code>, должны заворачиваться в <code>try ... catch()</code>, но об этом ниже, в секции &#8220;Валидация&#8221;. Метод возвращает текущий объект (<code>$this</code>).</li>
<li>Метод <code>update()</code> работает только с измененными данными (свойство $_changed). Кроме того, поля <strong>Sprig_Field_Timestamp</strong> со свойством <code>$auto_now_update == TRUE</code> будут заполнены текущей меткой времени. В целом все аналогично созданию записи &#8211; валидация измененных значений с дальнейшим сохранением в базе.</li>
<li>Метод <code>delete()</code> предназначен для удаления записей, и может быть использован в различных случаях. Если запись была загружена из БД, она будет удалена. Если же объект только что создан и не сохранен в БД, введенные в него значения будут использованы в качестве условия для удаления всех подходящих записей в БД. Если модель пустая, ничего не будет удалено (интересно, как тогда удалить все записи, т.к. метода <code>delete_all()</code> нет).</li>
</ul>
<h2>Настройка формата значений при выводе на экран</h2>
<p>Приятная &#8220;вкусность&#8221;. Для каждого поля можно менять форматирование значения, для чего используется метод <code>verbose($value)</code>. По умолчанию он выводит значение как строку, но есть и отличные от дефолтных методы, например для меток времени и картинок:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// Sprig_Field_Timestamp, использует свойство $format для хранения формата даты</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> verbose<span style="color: #009900;">&#40;</span><span style="color: #000088;">$value</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: #990000;">is_integer</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$value</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #990000;">date</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">format</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</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: #b1b100;">return</span> <span style="color: #0000ff;">''</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// Sprig_Field_Image, выводит полный путь к картинке, используя свойство $path</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> verbose<span style="color: #009900;">&#40;</span><span style="color: #000088;">$value</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;">path</span><span style="color: #339933;">.</span><span style="color: #000088;">$value</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code></p>
<p>Есть и другие классы с собственными обработчиками (для чисел с плавающей запятой будет произведено форматирование через <code>number_format()</code>, для логических выведение строковых &#8216;<em>TRUE</em>&#8216; или &#8216;<em>FALSE</em>&#8216;, а для <strong>Hasmany</strong> &#8211; вывод значений через запятую).</p>
<h2>Работа с формами</h2>
<p>Выбор элементов форм в зависимости от типа поля &#8211; одно из преимуществ <strong>Sprig</strong>. Каждый объект класса <strong>Sprig_Field</strong> должен реализовывать метод <code>input($name, $value, array $attr = NULL)</code>. По умолчанию будет выведен либо текстовый элемент <strong>input</strong>, либо выпадающий список <strong>select</strong> (если задано свойство <code>$choices</code>):</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> input<span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span> <span style="color: #000088;">$attr</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #666666; font-style: italic;">// Make the value verbose</span>
		<span style="color: #000088;">$value</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">verbose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$value</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: #990000;">is_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">choices</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">return</span> Form<span style="color: #339933;">::</span><span style="color: #004000;">select</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">choices</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #000088;">$attr</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: #b1b100;">return</span> Form<span style="color: #339933;">::</span><span style="color: #004000;">input</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #000088;">$attr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code><br />
Массив <code>$attr</code> позволяет передать дополнительные атрибуты элемента форм (например, &#8216;<em>class</em>&#8216; или &#8216;<em>tabindex</em>&#8216;). Метод возвращает код сгенерированного поля формы. Как видно, метод <code>input()</code> активно использует <code>verbose()</code>, так что не забывайте его менять для создаваемых Вами классов нестандартных полей.</p>
<p>Для того, чтобы лучше понять, как (и зачем) реализовать собственный метод для формирования контролов, приведу методы для картинок и логических полей:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// Sprig_Field_Image</span>
<span style="color: #666666; font-style: italic;">// Вывод поле для загрузки изображения. Если есть значение, то покажет и саму картинку</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> input<span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span> <span style="color: #000088;">$attr</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #000088;">$input</span> <span style="color: #339933;">=</span> Form<span style="color: #339933;">::</span><span style="color: #990000;">file</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$attr</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;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">value</span><span style="color: #009900;">&#41;</span>
		<span style="color: #009900;">&#123;</span>
			<span style="color: #000088;">$input</span> <span style="color: #339933;">.=</span> HTML<span style="color: #339933;">::</span><span style="color: #004000;">image</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">verbose</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #b1b100;">return</span> <span style="color: #000088;">$input</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// Sprig_Field_Boolean</span>
<span style="color: #666666; font-style: italic;">// если установлено свойство $append_label, будет добавлен текст к checkbox'у</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> input<span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span> <span style="color: #000088;">$attr</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #000088;">$checkbox</span> <span style="color: #339933;">=</span> Form<span style="color: #339933;">::</span><span style="color: #004000;">checkbox</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$name</span><span style="color: #339933;">,</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #000088;">$value</span><span style="color: #339933;">,</span> <span style="color: #000088;">$attr</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</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;">append_label</span><span style="color: #009900;">&#41;</span>
		<span style="color: #009900;">&#123;</span>
			<span style="color: #000088;">$checkbox</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">' '</span><span style="color: #339933;">.</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">label</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span>  <span style="color: #000088;">$checkbox</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p></code></p>
<p>Также есть метод <code>inputs($labels)</code>, который позволяет разом получить массив контролов. Параметр <code>$labels</code> указывает, использовать ли в качестве ключей данного массива сгенерированный код для меток (<code>$labels = TRUE</code>, по умолчанию), либо просто имена полей. Например, так:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$blog</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">inputs</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$label</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$input</span><span style="color: #009900;">&#41;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$label</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;&lt;br /&gt;&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$input</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;&lt;br /&gt;&quot;</span><span style="color: #339933;">;</span></pre></div></div>

<p></code></p>
<h2>Валидация</h2>
<p>Раз есть генерация форм, надо как-то обрабатывать введенные данные. Для этого есть возможность установки свойств валидации, кроме того не стоит забывать об автоматически добавляемых фильтров и правил валидации для отдельных стандартных классов (например, проверка корректности <em>email</em> для <strong>Sprig_Field_Email</strong>). Обратимся к все той же модели <strong>Sprig_Model_User</strong>, так там объявлено поле `<em>username</em>` в методе <code>_init()</code>:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #0000ff;">'username'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000000; font-weight: bold;">new</span> Sprig_Field_Char<span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
		<span style="color: #0000ff;">'empty'</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;">'unique'</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;">'rules'</span>  <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
			<span style="color: #0000ff;">'regex'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/^[\pL_.-]+$/ui'</span><span style="color: #009900;">&#41;</span>
		<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span></pre></div></div>

<p></code><br />
Поскольку внутри используется стандартный класс <strong>Validate</strong>, синтаксис объявления свойств привычен. Сама проверка запускается методом <code>check(array $data = NULL)</code>. Да, в <strong>Sprig</strong> можно (напрямую) передать в модель данные для валидации (тот же массив <code>$_POST</code>), однако это немного другой механизм, отличный от валидации в <strong>ORM</strong> версии <strong>2.3.4</strong>. Во время валидации сама модель не меняется, она предоставляет данные для создания объекта <strong>Validate</strong> (т.е. свойства <code>$label</code>, <code>$filters</code>, <code>$rules</code> и <code>$callbacks</code> соответствующих полей).</p>
<blockquote><p>Если параметр <code>$data</code> не указан, будут проверяться измененные (и несохраненные) данные модели. Таким образом, проверять только что загруженный из БД объект бессмысленно. </p></blockquote>
<p>Метод <code>check()</code> возвращает массив обработанные данные в случае успеха (они могут отличаться от исходных, например из-за фильтрации). А вот если выявлено несоответствие, будет сгенерировано исключение <strong>Validate_Exception</strong>, так что придется оборачивать вызовы <code>check()</code> в блоки <code>try ... catch</code>:</p>
<p><code></p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">try
    <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$object</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">check</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    catch <span style="color: #009900;">&#40;</span>Validate_Exception <span style="color: #000088;">$e</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$errors</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$e</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">array</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">errors</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'validation'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span></pre></div></div>

<p></code></p>
<blockquote><p>Объект <strong>Validate_Exception</strong> содержит свойство <code>$array</code>, в которое помещаются данные при генерации исключения. В данном случае это сам объект <strong>Validate</strong>, так что <code>$e->array->errors()</code> осуществляет запрос ошибок валидации.</p></blockquote>
<h2>Прочие полезные методы</h2>
<ul>
<li>Получить значения объекта как массив можно с помощью метода <code>as_array($verbose)</code>, где параметр <code>$verbose</code> определяет, применять ли метод <code>verbose()</code> на все поля, либо отдавать как есть (по умолчанию).</li>
<li>Проверить, менялось ли какое либо поле или значения модели в целом с момента последнего сохранения или загрузки, можно через метод <code>changed($field)</code>. Если параметр <code>$field</code> не указан, вернется массив измененных полей (имя поля => значение), иначе &#8211; логическое <strong>TRUE</strong> (поле поменялось) или <strong>FALSE</strong>.</li>
<li>Метод <code>loaded()</code> полезен для проверки, был ли объект загружен из БД или он просто создан &#8220;на лету&#8221;.</li>
<li>Метод <code>pk($table)</code> возвращает имя поля для первичного ключа. Если параметр $table указан, то он будет подставлен перед именем поля в качестве названия таблицы (значение TRUE используется для подстановки имени текущей таблицы модели). Также есть метод <code>tk($table)</code>, который возвращает т.н. &#8220;титульный&#8221; ключ, т.е. значение свойства <code>$_title_key</code>.</li>
<li>Для формирования выпадающих списков есть знакомый нам метод <code>select_list($key = NULL, $value = NULL)</code>. Он возвращает массив <code>$key => $value</code> <strong>всех</strong> записей в БД.</li>
<li>Метод <code>table()</code> возвращает имя таблицы модели.</li>
</ul>
<h2>Напоследок</h2>
<ul>
<li>Я нашел зачатки поддержки составных первичных ключей (свойство <code>$_primary_key</code> может быть массивом полей), но пока это не везде работает. Возможно, в следующих коммитах ситуация прояснится.</li>
<li>Также не увидел как использовать напрямую методы <strong>Database_Query_Builder</strong>, например <code>limit()</code> или <code>where()</code>. Возможно, они появятся позже. Единственный доступный способ &#8211; передача параметра <code>$query</code> (в котором можно настроить большинство необходимых условий) в метод <code>load()</code>.</li>
</ul>
<p>Дополнительную информацию Вы можете получить в коротком <a href="http://github.com/shadowhand/sprig/">ReadMe</a> на странице проекта, а также в данном <a href="http://forum.kohanaphp.com/comments.php?DiscussionID=3780&#038;page=2#Item_24">топике форума</a>. Конечно, в данной статье практически ничего не сказано про использование связей в <strong>Sprig</strong>. Это будет темой следующего поста.</p>

<div class="bookmarkz"><a href="http://www.google.com/bookmarks/mark?op=add&bkmk=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/google.png" border="0" width="16" height="16" alt="Google Bookmarks" title="Google Bookmarks"></a> <a href="http://digg.com/submit?url=http://brotkin.ru/2009/11/02/ko3-sprig/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/digg.png" border="0" width="16" height="16" alt="Digg" title="Digg"></a> <a href="http://reddit.com/submit?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/reddit.png" border="0" width="16" height="16" alt="Reddit" title="Reddit"></a> <a href="http://del.icio.us/post?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/delicious.png" border="0" width="16" height="16" alt="del.icio.us" title="del.icio.us"></a> <a href="http://ma.gnolia.com/beta/bookmarklet/add?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web&description=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/magnolia.png" border="0" width="16" height="16" alt="Ma.gnolia" title="Ma.gnolia"></a> <a href="http://www.technorati.com/faves?add=http://brotkin.ru/2009/11/02/ko3-sprig/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/technorati.png" border="0" width="16" height="16" alt="Technorati" title="Technorati"></a> <a href="http://www.slashdot.org/bookmark.pl?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/slashdot.png" border="0" width="16" height="16" alt="Slashdot" title="Slashdot"></a> <a href="http://myweb2.search.yahoo.com/myresults/bookmarklet?u=http://brotkin.ru/2009/11/02/ko3-sprig/&t=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/yahoo.png" border="0" width="16" height="16" alt="Yahoo My Web" title="Yahoo My Web"></a> <a href="http://news2.ru/add_story.php?url=http://brotkin.ru/2009/11/02/ko3-sprig/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/news2ru.png" border="0" width="16" height="16" alt="News2.ru" title="News2.ru"></a> <a href="http://www.bobrdobr.ru/addext.html?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/bobrdobr.png" border="0" width="16" height="16" alt="БобрДобр.ru" title="БобрДобр.ru"></a> <a href="http://rumarkz.ru/bookmarks/?action=add&popup=1&address=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/rumarkz.png" border="0" width="16" height="16" alt="RUmarkz" title="RUmarkz"></a> <a href="http://www.vaau.ru/submit/?action=step2&url=http://brotkin.ru/2009/11/02/ko3-sprig/" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/vaau.png" border="0" width="16" height="16" alt="Ваау!" title="Ваау!"></a> <a href="http://memori.ru/link/?sm=1&u_data[url]=http://brotkin.ru/2009/11/02/ko3-sprig/&u_data[name]=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/memori.png" border="0" width="16" height="16" alt="Memori.ru" title="Memori.ru"></a> <a href="http://www.rucity.com/bookmarks.php?action=add&address=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/rucity.png" border="0" width="16" height="16" alt="rucity.com" title="rucity.com"></a> <a href="http://moemesto.ru/post.php?url=http://brotkin.ru/2009/11/02/ko3-sprig/&title=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/moemesto.png" border="0" width="16" height="16" alt="МоёМесто.ru" title="МоёМесто.ru"></a> <a href="http://www.mister-wong.ru/index.php?action=addurl&bm_url=http://brotkin.ru/2009/11/02/ko3-sprig/&bm_description=Ko3%3A+%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C+Sprig.+%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B.+-+%D0%98%D0%B7%D1%83%D1%87%D0%B0%D0%B5%D0%BC+Web" rel="nofollow" target="_blank"><img src="http://brotkin.ru/wp-content/plugins/bookmarkz/images/mrwong.png" border="0" width="16" height="16" alt="Mister Wong" title="Mister Wong"></a> </div>
]]></content:encoded>
			<wfw:commentRss>http://brotkin.ru/2009/11/02/ko3-sprig/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

