<?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>Robert Speer Web Development &#187; Doctrine</title>
	<atom:link href="http://www.robertspeer.com/blog/category/web-development/doctrine/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.robertspeer.com/blog</link>
	<description>Symfony,  PHP, Wordpress, Business Analysis</description>
	<lastBuildDate>Sat, 07 Jan 2012 22:29:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Internationalized (i18n) Admin Generator CRUD&#8217;s in Symfony 1.2.9 + Doctrine</title>
		<link>http://www.robertspeer.com/blog/i18nadmingenerator/</link>
		<comments>http://www.robertspeer.com/blog/i18nadmingenerator/#comments</comments>
		<pubDate>Sun, 04 Oct 2009 22:50:43 +0000</pubDate>
		<dc:creator>Robert Speer</dc:creator>
				<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.robertspeer.com/blog/?p=64</guid>
		<description><![CDATA[I was having some trouble finding documentation on how to i18n generated CRUD&#8217;s, so once I figured (most) of it out I thought I&#8217;d share it The Example Application Since I have to create a feature in my one of my current work projects to store random bits of content, like privacy policies and such, [...]]]></description>
			<content:encoded><![CDATA[<p>I was having some trouble finding documentation on how to i18n generated CRUD&#8217;s, so once I figured (most) of it out I thought I&#8217;d share it</p>
<h2><span style="font-weight: normal;">The Example Application</span></h2>
<p><a href="http://www.robertspeer.com/i18n CRUD edit view.jpg" target="_blank"><img class="size-medium wp-image-69 alignright" title="i18n CRUD edit view" src="http://www.robertspeer.com/blog/wp-content/uploads/2009/10/i18n-CRUD-edit-view-168x300.jpg" alt="Content Block CRUD with French langage selected" width="168" height="300" /></a></p>
<p>Since I have to create a feature in my one of my current work projects to store random bits of content, like privacy policies and such, in multiple languages. I thought I&#8217;d double dip and use that for this example.  I&#8217;m calling the feature content blocks.  It will have a backend CRUD that will facilitate translations.  The UI I needed was to have the default language show up as well as one of the many languages this information would be translated into.  My app has the possibility of having more than 20 language options so putting them all in the CRUD at once was unreasonable.<span id="more-64"></span></p>
<p>The example to the left shows what the CRUD looks like when French is set as the user&#8217;s culture.  If the default language is chosen a second language form does not show up.</p>
<p>On the frontend I&#8217;m just going to do a simple data pull for this example.  Both the frontend and the backend app will have very simple language switchers  to demonstrate how that works.<br />
Now that I now how this works it&#8217;s actually pretty darn simple, however figuring it out took longer than I&#8217;d like.  Hopefully this tutorial will save you some time.</p>
<p>I&#8217;m going to skip the application setup, if you don&#8217;t know how to do that I used the same steps that are in the Doctrine version of the <a href="http://www.symfony-project.org/jobeet/1_2/Doctrine/en/01">Jobeet tutorial</a>.</p>
<p><a href="http://www.robertspeer.com/i18n_demo.zip" target="_self">Download the example app zip file here, includes Symfony 1.2.9</a></p>
<h2>Doctrine schema.yml</h2>
<p>The database schema.yml was by a little tricky at first.  I did not realize that Doctrine handles I18n tables so much differently from Propel.  With Propel I would have defined a second table named content_block_i18n and put the translated fields there.  For Doctrine they simply go in under the actAs and I18n.  This is less typing and I suspect more intuitive for those who don&#8217;t already know Propel.</p>
<p>Also remember to put columns: before your field definitions, and leave out the connection at the top of the file.  Timestammable adds the created_at &amp; updated_at fields.  Also notice that the data types are different from Propel&#8217;s.</p>
<p>I think I&#8217;m going to like these changes, but they are different so be careful if you are used to Propel.<br />
<code><br />
content_block:<br />
actAs:<br />
Timestampable: ~<br />
I18n:<br />
fields: [short_title, title, extract, content]<br />
columns:<br />
weight: integer<br />
active: boolean<br />
short_title: string(50)<br />
title:       string<br />
extract:     string<br />
content:     string(4000)<br />
</code><br />
Once you&#8217;re done: create your database, edit databases.yml, build-all, and clear you cache.  Details are in the Jobeet tutoral in <a href="http://www.symfony-project.org/jobeet/1_2/Doctrine/en/03" target="_blank">Day 3: The Data Model</a>.</p>
<h2>Adding embedI18n() to the form class a.k.a: where the magic happens</h2>
<p>This part took some serious research, I was just sure all I had to do was edit something in the generator.yml, but that turned out not to be the case.</p>
<p>I finally found  embedI18n() in the Forms in Action book in the i18n chapter under <a href="http://www.symfony-project.org/forms/1_2/en/08-Internationalisation-and-Localisation#chapter_08_propel_objects_internationalization">Propel Objects Internationalization</a>.  It does use the much maligned sfContext, and if you know a better way write a comment.</p>
<p>You&#8217;ll want to generate forms(php symfony doctrine:generate-forms) &amp; then the contend block CRUD (php symfony doctrine:generate-admin backend ContentBlock), as well as turn I18n on in the backend settings.yml.</p>
<p>This is in: lib/form/content_blockForm.class in the example application.<br />
<code><br />
/**<br />
* content_block form.<br />
*<br />
* @package    form<br />
* @subpackage content_block<br />
*/<br />
class content_blockForm extends Basecontent_blockForm<br />
{<br />
/**<br />
* Form configuration settings<br />
*<br />
* @author Robert H. Speer<br />
*/<br />
public function configure()<br />
{<br />
$this-&gt;embedI18n(array(sfConfig::get('sf_default_culture', 'en'),<br />
$this-&gt;getCurrentCulture())<br />
);<br />
}<br />
</code><br />
<code><br />
/**<br />
* pulls the current culture from the user object<br />
*<br />
* @return string<br />
* @author Robert H. Speer<br />
*<br />
* Notes:<br />
*  RHS 10/2/09 - sfContext::getInstance() violates MVC but I don't know a way<br />
*                around it ATM.<br />
*/<br />
public function getCurrentCulture()<br />
{<br />
$culture = sfContext::getInstance()-&gt;getUser()-&gt;getCulture();</code></p>
<p><code>if (strlen($culture)&gt;0) { // return user selected language<br />
return $culture;<br />
}else{ // return default culture, or defaults to english<br />
return sfConfig::get('sf_default_culture', 'en');<br />
}<br />
}</code></p>
<p><code>}</code></p>
<p><code> </code></p>
<h2>A simple language switcher component</h2>
<p>I&#8217;ve included a very simple language switching component in the example application.  Assuming you know <a href="http://www.symfony-project.org/book/1_2/07-Inside-the-View-Layer#chapter_07_sub_components" target="_self">how to write a component</a>, it&#8217;s not a big deal.</p>
<p>The actual language setter is in both apps (i know wet is bad) under language_switcher/actions/action.class.php &amp; looks like this:<br />
<code><br />
/**<br />
* changes the users culture and redirects them back the their previous page<br />
*<br />
* @author Robert H. Speer<br />
*/<br />
public function executeLanguage() {<br />
$this-&gt;getUser()-&gt;setCulture($this-&gt;getRequestParameter('culture'));</code></p>
<p><code> </code><code> $url = $this-&gt;getRequest()-&gt;getReferer() != '' ? $this-&gt;getRequest()-&gt;getReferer() : '@homepage';<br />
$this-&gt;redirect($url);<br />
}<br />
</code><br />
It&#8217;s going to take the user&#8217;s selected culture set that to the user object, and then redirect causing a refresh.</p>
<h2>How to get at that translated content</h2>
<p>This is the easy part, you actually don&#8217;t have to do anything special to grab content in the language set in the user object, just get the object and call the getter.</p>
<p>Grab the object(s) with something like this, but preferably in the model layer instead of apps/frontend/homepage/actions/action.class.php:<br />
<code><br />
/**<br />
* Executes index action<br />
*<br />
* @param sfRequest $request A request object<br />
*/<br />
public function executeIndex(sfWebRequest $request)<br />
{<br />
$this-&gt;block = Doctrine::getTable('content_block')-&gt;createQuery('a')-&gt;execute();<br />
}<br />
</code><br />
Then in your template you can access all the fields just like if they were in the same table (this is really cool):<br />
<code><br />
/**<br />
* Very simple homepage for demo purposes only<br />
*<br />
* @author Robert H. Speer<br />
*/<br />
foreach ($block as $key=&gt;$row)<br />
{<br />
echo 'id: '.$row-&gt;getId().'&lt;br&gt;';<br />
echo 'weight: '.$row-&gt;getWeight().'&lt;br&gt;';<br />
echo 'short title: '.$row-&gt;getShortTitle().'&lt;br&gt;';<br />
echo 'title: '.$row-&gt;getTitle().'&lt;br&gt;';<br />
echo 'extract: '.$row-&gt;getExtract().'&lt;br&gt;';<br />
echo 'content: '.$row-&gt;getContent().'&lt;br&gt;';<br />
echo 'created at: '.$row-&gt;getCreatedAt().'&lt;br&gt;';<br />
echo 'updated at: '.$row-&gt;getUpdatedAt().'&lt;br&gt;';<br />
echo 'lang: '.$row-&gt;getLang().'&lt;br&gt;';<br />
echo '&lt;hr&gt;';<br />
}</code></p>
<p>If you get the example app going on your own machine add a few records with some translations, then change the language with the language drop down and it will just work automagicaly.  For your own apps remember to turn I18n on in your applications settings.yml.</p>
<h2>Reference Links</h2>
<ul>
<li>As always the Symfony&#8217;s sweet <a href="http://www.symfony-project.org/doc/1_2/">documentation</a></li>
<li>Sandbox Web Solutions does a great job of explaining a <a href="http://sandbox-ws.com/frameworks/symfony-frameworks/how-to-embed-forms-in-symfony-12-admin-generator-part-3" target="_blank">different way to embed i18n forms in a generated CRUD</a></li>
</ul>
<h2>What I have not figured out yet</h2>
<ul>
<li>I have got the file upload widget to show up in the Admin generator but I it does not work automagicaly, like I think it should, I think I&#8217;m going to have to write the file handler myself.
<ul>
<li><em>edit 10/04/09 10:42PM</em> -&gt; <a href="http://stereointeractive.com/blog/2008/12/23/symfony-12-upload-a-file-inside-an-embedded-form/" target="_blank">Stereo Interactive has a work around for this</a></li>
</ul>
</li>
<li>Getting at the embedded fields in the generator.yml is elusive as well.</li>
</ul>
<p>I&#8217;ll be working on both of these problems as soon as I get back to work, so hopefully I&#8217;ll have an update soon.  If you figure it out first please write a comment.</p>
<h3><em>Disclaimer:</em></h3>
<p><em>By the time I was done writing this tutorial I was very ready to not be at my computer anymore, there are going to be some grammatical mistakes and maybe some code ones as well.  I through this together on WAMP, on my home desktop, so you may have to change the slashes on your path, and update your apache conf &amp;/or your .htaccess file to get it to work.  The application I&#8217;ve uploaded does work, but it is just a demo so don&#8217;t trust it too much <img src='http://www.robertspeer.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.robertspeer.com/blog/i18nadmingenerator/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

