<?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>Doctrine | Nerdpress.org</title>
	<atom:link href="https://nerdpress.org/tag/doctrine/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerdpress.org</link>
	<description>...dev, tech problems and solutions.</description>
	<lastBuildDate>Fri, 17 Jan 2025 16:34:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Doctrine: WHERE IN with array of integers</title>
		<link>https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Fri, 17 Jan 2025 16:34:32 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Sql]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3381</guid>

					<description><![CDATA[<p>Since I stumbled across this the other day, here&#8217;s how you build an SQL query with a WHERE IN condition for an array of integers. This is surprisingly not intuitive, as you cannot simply pass an array of integers to a prepared statement. Instead, you need to add special binding types to inform Doctrine that &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/" class="more-link">Continue reading<span class="screen-reader-text"> "Doctrine: WHERE IN with array of integers"</span></a></p>
The post <a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/">Doctrine: WHERE IN with array of integers</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Since I stumbled across this the other day, here&#8217;s how you build an SQL query with a <code>WHERE IN</code> condition for an array of integers.</p>



<p>This is surprisingly not intuitive, as you cannot simply pass an array of integers to a prepared statement. Instead, you need to add special binding types to inform Doctrine that this is indeed an array of integers.</p>



<p>It&#8217;s very well explained in the documentation under the Parameters Conversion section:<br /><a href="https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion">https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion</a><br />But it is rather hard to find and it took me a while.</p>



<p>The trick is to add the array binding types to the the types parameters to the query method. In the case of integers, it is<code> \Doctrine\DBAL\ArrayParameterType::INTEGER</code>. <br />Now Doctrine knows how to handle the types in the array while binding the values.</p>



<span id="more-3381"></span>



<p>Example:</p>


<pre class="wp-block-code"><span><code class="hljs language-php">$sql = <span class="hljs-string">'SELECT * FROM items WHERE id IN (?)'</span>; 
$numbersAsStrings = <span class="hljs-string">"1,2,3"</span>;
$numbers = array_map(<span class="hljs-string">'intval'</span>, explode(<span class="hljs-string">','</span>, $numbersAsStrings));
$users = $con-&gt;fetchAllAssociative(
  $sql, 
  &#91;$numbers], 
  &#91;ArrayParameterType::INTEGER]
);</code></span></pre>


<p>If you omit the types, Doctrine will simply try to bind the array as string, which will fail and you get this error:<br /><em>Array to string conversion</em></p>



<p>If you try to pass it as a comma-separated string yourself and omit the types like <kbd><code>$numbers = implode(',',array_map('intval', explode(',', $numbersAsStrings)));</code></kbd> You will get an error because Doctrine expects an array of values for the <kbd>IN</kbd> clause and not a string.<br />Error: <br /><em>count(): Argument #1 ($value) must be of type Countable|array, string given</em></p>



<p>I hope this helps someone finding out how to make <kbd>WHERE IN</kbd> SQL queries with Doctrine more quickly than me next time.</p>



<p></p>The post <a href="https://nerdpress.org/2025/01/17/doctrine-where-in-with-array-of-integers/">Doctrine: WHERE IN with array of integers</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Doctrine migrations and Postgis</title>
		<link>https://nerdpress.org/2022/10/31/doctrine-migrations-and-postgis/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Mon, 31 Oct 2022 09:12:46 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Postgis]]></category>
		<category><![CDATA[Postgres]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3184</guid>

					<description><![CDATA[<p>Using Postgres with the Postgis extension to integrate GeoData / GIS functionality in your project is not natively supported by Doctrine and Doctrine migrations. First you have to add the extension to Postgres, even if you use the Postgis docker image like postgis/postgis:14-3.3-alpine. So add this SQL statement to the up method of your first &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2022/10/31/doctrine-migrations-and-postgis/" class="more-link">Continue reading<span class="screen-reader-text"> "Doctrine migrations and Postgis"</span></a></p>
The post <a href="https://nerdpress.org/2022/10/31/doctrine-migrations-and-postgis/">Doctrine migrations and Postgis</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Using Postgres with the Postgis extension to integrate GeoData / GIS functionality in your project is not natively supported by Doctrine and Doctrine migrations.</p>



<p>First you have to add the extension to Postgres, even if you use the Postgis docker image like <code>postgis/postgis:14-3.3-alpine</code>.</p>



<p>So add this SQL statement to the up method of your first migration:<br /><code>$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis;');</code><br /></p>



<p>and the DROP statement for the extension to the down method:<br /><code>$this->addSql('DROP EXTENSION postgis;');</code></p>



<p>Now, when using Doctrine with Postgres and Postgis extension, migrations still behave a bit odd and try to remove Sequences created by Postgis, because Doctrine migrations does not take Postgis extension&#8217; s built-in Sequences into account.</p>



<span id="more-3184"></span>



<p>So you will find this statements in your first migration, which is basically wrong because &#8211;<em>of course</em>&#8211; we want to keep the topology and tiger Sequences.</p>


<pre class="wp-block-code"><span><code class="hljs language-php">	<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">up</span><span class="hljs-params">(Schema $schema)</span>: <span class="hljs-title">void</span>
	</span>{
		<span class="hljs-comment">// this up() migration is auto-generated, please modify it to your needs</span>
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE topology.topology_id_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.county_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.state_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.place_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.cousub_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.edges_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.addrfeat_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.faces_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.featnames_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.addr_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.zcta5_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.tract_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.tabblock_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.bg_gid_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.pagc_gaz_id_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.pagc_lex_id_seq CASCADE'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'DROP SEQUENCE tiger.pagc_rules_id_seq CASCADE'</span>);
	}

	<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">down</span><span class="hljs-params">(Schema $schema)</span>: <span class="hljs-title">void</span>
	</span>{
		<span class="hljs-comment">// this down() migration is auto-generated, please modify it to your needs</span>
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SCHEMA topology'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SCHEMA tiger'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SCHEMA tiger_data'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE topology.topology_id_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.county_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.state_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.place_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.cousub_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.edges_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.addrfeat_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.faces_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.featnames_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.addr_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.zcta5_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.tract_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.tabblock_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.bg_gid_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.pagc_gaz_id_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.pagc_lex_id_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
		<span class="hljs-keyword">$this</span>-&gt;addSql(<span class="hljs-string">'CREATE SEQUENCE tiger.pagc_rules_id_seq INCREMENT BY 1 MINVALUE 1 START 1'</span>);
	}</code></span></pre>


<p>So we have to teach Doctrine to respect these Postgis features.<br />Therefor extend the PostgreSqlPlatform class, add the Postgis features as exclude condition to the query:</p>



<p>src/DBAL/PostgisPostgreSqlPlatform.php:</p>


<pre class="wp-block-code"><span><code class="hljs language-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">DBAL</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Doctrine</span>\<span class="hljs-title">DBAL</span>\<span class="hljs-title">Platforms</span>\<span class="hljs-title">PostgreSQLPlatform</span> <span class="hljs-title">as</span> <span class="hljs-title">PostgreSqlPlatformBase</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostgisPostgreSqlPlatform</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PostgreSqlPlatformBase</span>
</span>{
	<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getListNamespacesSQL</span><span class="hljs-params">()</span>
	</span>{
		<span class="hljs-comment"># exclude postgis schemas</span>
		<span class="hljs-keyword">return</span> <span class="hljs-string">"SELECT schema_name AS nspname
				FROM   information_schema.schemata
				WHERE  schema_name NOT LIKE 'pg\_%'
				AND schema_name NOT LIKE 'topology'
				AND schema_name NOT LIKE 'tiger%'
				AND    schema_name != 'information_schema'"</span>;
	}

	<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getListSequencesSQL</span><span class="hljs-params">($database)</span>
	</span>{
		<span class="hljs-keyword">return</span> <span class="hljs-string">'SELECT sequence_name AS relname,
					   sequence_schema AS schemaname,
					   minimum_value AS min_value, 
					   increment AS increment_by
				FROM   information_schema.sequences
				WHERE  sequence_catalog = '</span> . <span class="hljs-keyword">$this</span>-&gt;quoteStringLiteral($database) . <span class="hljs-string">"
				AND    sequence_schema NOT LIKE 'pg\_%' 
				AND sequence_schema NOT LIKE 'topology%'
				AND sequence_schema NOT LIKE 'tiger%'
				AND    sequence_schema != 'information_schema'"</span>;
	}
}</code></span></pre>


<p>As Gist: <a href="https://gist.github.com/ivoba/74a143d8074110ef47f93a581bb0c3f6" target="_blank" rel="noreferrer noopener">https://gist.github.com/ivoba/74a143d8074110ef47f93a581bb0c3f6</a></p>



<p>Now tell doctrine to use this PostgreSqlPlatform class instead.<br />In symfony you can add this to config/packages/doctrine.yaml:</p>


<pre class="wp-block-code"><span><code class="hljs language-yaml"><span class="hljs-attr">doctrine:</span>
	<span class="hljs-attr">dbal:</span>
		<span class="hljs-attr">url:</span> <span class="hljs-string">'%env(resolve:DATABASE_URL)%'</span>
		<span class="hljs-attr">types:</span>
			<span class="hljs-attr">geometry:</span> <span class="hljs-string">LongitudeOne\Spatial\DBAL\Types\GeometryType</span>
			<span class="hljs-attr">point:</span> <span class="hljs-string">LongitudeOne\Spatial\DBAL\Types\Geometry\PointType</span>
			<span class="hljs-attr">polygon:</span> <span class="hljs-string">LongitudeOne\Spatial\DBAL\Types\Geometry\PolygonType</span>
			<span class="hljs-attr">linestring:</span> <span class="hljs-string">LongitudeOne\Spatial\DBAL\Types\Geometry\LineStringType</span>
		<span class="hljs-attr">platform_service:</span> <span class="hljs-string">App\DBAL\PostgisPostgreSqlPlatform</span></code></span></pre>


<p>This is inspired by this Gist, which does the same for other Postgres extensions: <a href="https://gist.github.com/dextervip/a2f384050748d6ee3ed7d573425e9d58" target="_blank" rel="noreferrer noopener">https://gist.github.com/dextervip/a2f384050748d6ee3ed7d573425e9d58</a></p>



<p>Now you can run your migrations diff <code>bin/console do:mi:di</code> and the Postgis sequences will be respected by the Doctrine.</p>The post <a href="https://nerdpress.org/2022/10/31/doctrine-migrations-and-postgis/">Doctrine migrations and Postgis</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Migrating user table from Mysql to Postgres with Symfony and Doctrine</title>
		<link>https://nerdpress.org/2021/11/10/migrating-user-table-from-mysql-to-postgres-with-symfony-and-doctrine/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Wed, 10 Nov 2021 08:46:07 +0000</pubDate>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Postgres]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3087</guid>

					<description><![CDATA[<p>When using bin/console make:entity on Mysql and then later you switch your application to Postgres and you have a table called user, which you most likely have when using security component of Symfony.Then you will receive an error because user is a reserved word in Postgres! An exception occurred while executing 'INSERT INTO user (id, &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2021/11/10/migrating-user-table-from-mysql-to-postgres-with-symfony-and-doctrine/" class="more-link">Continue reading<span class="screen-reader-text"> "Migrating user table from Mysql to Postgres with Symfony and Doctrine"</span></a></p>
The post <a href="https://nerdpress.org/2021/11/10/migrating-user-table-from-mysql-to-postgres-with-symfony-and-doctrine/">Migrating user table from Mysql to Postgres with Symfony and Doctrine</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>When using <code>bin/console make:entity</code> on Mysql and then later you switch your application to Postgres and you have a table called <code>user</code>, which you most likely have when using security component of Symfony.<br />Then you will receive an error because <code>user</code> is a reserved word in Postgres!</p>



<p><code>An exception occurred while executing 'INSERT INTO user (id, email, roles, password, is_verified) VALUES (?, ?, ?, ?, ?)' with params [3, "dev@dev.de", "[]", "your-encrypted-password", 0]:<br />SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "user"<br />LINE 1: INSERT INTO user (id, email, roles, password, is_verified) V...</code></p>



<span id="more-3087"></span>



<p>To fix this you have to escape the table name on your entity, fe. User.php:<br /><br />@<code>ORM\Table(name="`user`")</code></p>



<p>(<strong>note the backticks inside the quotes!)</strong></p>



<p>If you generate the entity with <a href="https://symfony.com/bundles/SymfonyMakerBundle/current/index.html" target="_blank" rel="noreferrer noopener">maker bundle</a>: <code>bin/console make:entity</code> directly on Postgres the backticks are added automatically.<br />But not when you switch the DB type. Then you have to add them manually. :)</p>



<p><br /></p>The post <a href="https://nerdpress.org/2021/11/10/migrating-user-table-from-mysql-to-postgres-with-symfony-and-doctrine/">Migrating user table from Mysql to Postgres with Symfony and Doctrine</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Silex and MongoDB simply</title>
		<link>https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/</link>
					<comments>https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/#comments</comments>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Mon, 30 Jan 2012 14:27:34 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Silex]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[silex]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=2033</guid>

					<description><![CDATA[<p>Using MongoDB in your Silex Project is quite easy. I will show this with my Superleansilexplate and will integrate it there as an example. Since i dont want to integrate MongoDB in Superleansilexplate it will just become an additional gist. Given you have some smaller amount of data like a counter that needs to be &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/" class="more-link">Continue reading<span class="screen-reader-text"> "Silex and MongoDB simply"</span></a></p>
The post <a href="https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/">Silex and MongoDB simply</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Using MongoDB in your Silex Project is quite easy.</p>
<p>I will show this with my <a href="https://github.com/ivoba/superleansilexplate">Superleansilexplate</a> and will integrate it there as an example.<br />
Since i dont want to integrate MongoDB in Superleansilexplate it will just become an additional <a href="https://gist.github.com/1704512">gist</a>.</p>
<p>Given you have some smaller amount of data like a counter that needs to be stored or other loose coupled datasets, we simply speak to MongoDB &#8220;directly&#8221; and store the data via <a href="https://github.com/doctrine/mongodb">Doctrine MongoDB Abstraction Layer</a>.<br />
Since i presume the Data / Document Structure isnt that complex we dont use <a href="https://github.com/doctrine/mongodb-odm">Doctrine MongoDB ODM</a> (the Object Document Mapper).<br />
If you want to use it instead, try this <a href="https://github.com/docteurklein/SilexExtensions">Silex Extensions</a>.</p>
<p><span id="more-2033"></span></p>
<p>So we use this <a href="https://github.com/fate/Silex-Extensions">SilexExtensions</a> collection and install it via git:</p>
<pre class="brush: bash; title: ; notranslate">
 cd mysilexproject
 git clone git@github.com:fate/Silex-Extensions.git vendor/silex-extension
</pre>
<p>Then we install the doctrine mongodb libary manually:</p>
<pre class="brush: bash; title: ; notranslate">
 git clone --recursive https://github.com/doctrine/mongodb vendor/mongodb
</pre>
<p>The extension collection has some more poviders but you can just ignore them and just focus on the MongoDbExtension.</p>
<p>&#8211;<em> i actually dont like extensions collections. i would prefer to clone each extensions individually. thou its not much code overhead, but when it comes to the vendor dependencies it can become quite messy IMHO</em> &#8211;</p>
<p>Alright now the code.<br />
Edit src/app.php and register the namespace for the extension:</p>
<pre class="brush: php; title: ; notranslate">
 $app&#x5B;'autoloader']-&gt;registerNamespace('SilexExtension', __DIR__ . '/../vendor/silex-extension/src');
</pre>
<p>Then register the MongoDbExtension;</p>
<pre class="brush: php; title: ; notranslate">
 $app-&gt;register(new SilexExtension\MongoDbExtension(), array(
 'mongodb.class_path' =&gt; __DIR__ . '/../vendor/mongodb/lib',
 'mongodb.connection' =&gt; array(
 'server' =&gt; 'mongodb://mysecretuser:mysecretpassw@localhost',
 'options' =&gt; array(),
 'eventmanager' =&gt; function($eventmanager) {
 }
 )
 ));
</pre>
<p>and finally query your MongoDB collection of choice and display the resultset as json:</p>
<pre class="brush: php; title: ; notranslate">
$app-&gt;get('/data-from-mongodb', function() use($app) {

$coll = $app&#x5B;'mongodb']-&gt;selectDatabase('mydb')-&gt;selectCollection('mycoll');
 $query = array(
 'query' =&gt; 'value'
 );
 $sort = array(
 'count' =&gt; -1,
 );
 $r = $coll-&gt;find($query)-&gt;sort($sort)-&gt;toArray();
 $response = new Response();
 $response-&gt;headers-&gt;set('Content-type', 'application/json');
 $response-&gt;setContent(json_encode($r));
 return $response;
 });
</pre>
<p>Easy</p>The post <a href="https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/">Silex and MongoDB simply</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2012/01/30/silex-and-mongodb-simply/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Distinct in Doctrine</title>
		<link>https://nerdpress.org/2011/04/16/distinct-in-doctrine/</link>
					<comments>https://nerdpress.org/2011/04/16/distinct-in-doctrine/#comments</comments>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Sat, 16 Apr 2011 13:51:33 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Sql]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1454</guid>

					<description><![CDATA[<p>Wenn man DISTINCT in einem Query und Doctrine nutzen will muss man mit Aliasen arbeiten! Sonst baut Doctrine einem da immer die id mit in den Query und das DISTINCT wird damit ausgehebelt. So gehts nicht: Doctrine::getTable('propose') -&#62;createQuery('propose') -&#62;select('propose.cat') -&#62;distinct() -&#62;fetchArray(); Denn das wird dazu: SELECT DISTINCT p.id AS p__id, p.cat AS p__cat FROM propose &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2011/04/16/distinct-in-doctrine/" class="more-link">Continue reading<span class="screen-reader-text"> "Distinct in Doctrine"</span></a></p>
The post <a href="https://nerdpress.org/2011/04/16/distinct-in-doctrine/">Distinct in Doctrine</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Wenn man <strong>DISTINCT</strong> in einem Query und Doctrine nutzen will <em>muss</em> man mit Aliasen arbeiten!<br />
Sonst baut Doctrine einem da immer die <strong>id</strong> mit in den Query und das <strong>DISTINCT</strong> wird damit ausgehebelt.</p>
<p><span id="more-1454"></span></p>
<p>So gehts nicht:</p>
<pre class="brush: php; title: ; notranslate">
Doctrine::getTable('propose')
-&gt;createQuery('propose')
-&gt;select('propose.cat')
-&gt;distinct()
-&gt;fetchArray();
</pre>
<p>Denn das wird dazu:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT DISTINCT p.id AS p__id, p.cat AS p__cat FROM propose p
</pre>
<p>so gehts:</p>
<pre class="brush: php; title: ; notranslate">
Doctrine::getTable('propose')
-&gt;createQuery('propose')
-&gt;select('propose.cat as cat')
-&gt;distinct()
-&gt;fetchArray();
</pre>
<p>Denn das wird dazu:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT DISTINCT p.cat AS p__0 FROM propose p
</pre>The post <a href="https://nerdpress.org/2011/04/16/distinct-in-doctrine/">Distinct in Doctrine</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2011/04/16/distinct-in-doctrine/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Symfony 2 Standard Edition released</title>
		<link>https://nerdpress.org/2011/03/07/symfony-2-standard-edition-released/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 07 Mar 2011 17:20:38 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Doctrine 2]]></category>
		<category><![CDATA[Standard Edition]]></category>
		<category><![CDATA[symfony 2]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=1401</guid>

					<description><![CDATA[<p>Ab heute, dem 7. 3. 2011, steht auf http://symfony.com die &#8220;Standard-Edition&#8221; der neuesten Version 2 des populären RAD-Frameworks zum Download bereit. Bereits am vergangenen Wochenende ging die neue Website des Projekts online. Symfony 2 wird als Sammlung loser gekoppelter Komponenten &#8211; sog. &#8220;Bundles&#8221; &#8211; in mehreren Ausgaben erhältlich sein. Zum jetzigen Zeitpunkt existiert bereits eine &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2011/03/07/symfony-2-standard-edition-released/" class="more-link">Continue reading<span class="screen-reader-text"> "Symfony 2 Standard Edition released"</span></a></p>
The post <a href="https://nerdpress.org/2011/03/07/symfony-2-standard-edition-released/">Symfony 2 Standard Edition released</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Ab heute, dem 7. 3. 2011, steht auf http://symfony.com die &#8220;Standard-Edition&#8221; der neuesten Version 2 des populären RAD-Frameworks zum Download bereit. Bereits am vergangenen Wochenende ging die neue Website des Projekts online.<br />
<span id="more-1401"></span><br />
Symfony 2 wird als Sammlung loser gekoppelter Komponenten &#8211; sog. &#8220;Bundles&#8221; &#8211; in mehreren Ausgaben erhältlich sein. Zum jetzigen Zeitpunkt existiert bereits eine Sandbox und eine davon abgeleitete &#8220;Standard-Edition&#8221;, die wie gewohnt ein festes Code-Verzeichnislayout vorgeben.</p>
<p>Als zukünftige Ausgaben denkbar sind ein Symfony2-basierendes CMF (Content Management Framework) oder eine auf die speziellen Befürfnisse von Online-Shops zugeschnittene Bundle-Sammlung.</p>
<p>Der Code ist noch nicht als stabil deklariert. Die bereits vorhandene und wie immer intensiv gepflegte Referenz-Dokumentation lädt bereits jetzt zum Reinschnuppern und Ausprobieren ein.  </p>The post <a href="https://nerdpress.org/2011/03/07/symfony-2-standard-edition-released/">Symfony 2 Standard Edition released</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The only valid measurement of code quality: WTFs/minute</title>
		<link>https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/</link>
					<comments>https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/#comments</comments>
		
		<dc:creator><![CDATA[Max Girkens]]></dc:creator>
		<pubDate>Wed, 17 Feb 2010 09:25:43 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[EntityManager]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[writeBehind]]></category>
		<category><![CDATA[WTFs]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=810</guid>

					<description><![CDATA[<p>oder: Doctrine 2 wird wohl ziemlich gut. Hier gibt es ein paar Notizen von Jonathan Wage&#8217;s Präsentation dazu! Sehr cool klingt unter anderem auch die writeBehind Geschichte und dass Objekte nicht mehr von Doctrine Klassen abgeleitet werden müssen, sondern mit dem &#8220;Entity Managers&#8221; zB. gespeichert werden. Also keine ->save() methoden mehr. :) “Everything is an &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/" class="more-link">Continue reading<span class="screen-reader-text"> "The only valid measurement of code quality: WTFs/minute"</span></a></p>
The post <a href="https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/">The only valid measurement of code quality: WTFs/minute</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>oder: <a href="http://www.doctrine-project.org/">Doctrine</a> 2 wird wohl ziemlich gut.</p>
<p><a href="http://window.punkave.com/2010/02/16/doctrine-2-jonathan-wage-at-sflive2010/">Hier</a> gibt es ein paar Notizen von Jonathan Wage&#8217;s Präsentation dazu!</p>
<p><span id="more-810"></span><br />
Sehr cool klingt unter anderem auch die writeBehind Geschichte und dass Objekte nicht mehr von Doctrine Klassen abgeleitet werden müssen, sondern mit dem &#8220;Entity Managers&#8221; zB. gespeichert werden.<br />
Also keine ->save() methoden mehr. :)</p>
<blockquote><p>
“Everything is an entity.” A lightweight persistent domain object, a regular PHP class. You don’t extend a base doctrine class. No final methods.</p>
<p>namespace Entities;<br />
class User<br />
{<br />
  private $id;<br />
  private $name;<br />
  private $address;<br />
}</p>
<p>Note use of <a href="http://de.php.net/manual/de/language.namespaces.php">namespaces</a>.</p>
<p>EntityManager is central access point to ORM. Used to query for persistent objects. Employs transactional write behind strategy that delays execution of SQL to execute it in the most efficient way.
</p></blockquote>The post <a href="https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/">The only valid measurement of code quality: WTFs/minute</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2010/02/17/the-only-valid-measurement-of-code-quality-wtfs-minute/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Etwas untergegangen: Propel 1.4 is raus</title>
		<link>https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/</link>
					<comments>https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/#comments</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 12 Nov 2009 19:54:02 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Propel 1.4]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=576</guid>

					<description><![CDATA[<p>&#8230; und hier gibt&#8217;s ein Changelog: http://propel.phpdb.org/trac/wiki/Users/Documentation/1.4/WhatsNew Erst in den nächsten Monaten wird sich wohl herausstellen, ob der neue Maintainer da auch langfristig Böcke drauf hat. Ich glaube, ich bleibe nach dem ganzen Lernaufwand jetzt erstmal bei Doctrine, da tut sich ja auch noch einiges (Obwohl ich stiller Fan der Criteria-API bin, und Doctrine mit &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/" class="more-link">Continue reading<span class="screen-reader-text"> "Etwas untergegangen: Propel 1.4 is raus"</span></a></p>
The post <a href="https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/">Etwas untergegangen: Propel 1.4 is raus</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>&#8230; und hier gibt&#8217;s ein Changelog: <a href="http://propel.phpdb.org/trac/wiki/Users/Documentation/1.4/WhatsNew">http://propel.phpdb.org/trac/wiki/Users/Documentation/1.4/WhatsNew<br />
</a><br />
<span id="more-576"></span></p>
<p>Erst in den nächsten Monaten wird sich wohl herausstellen, ob der neue Maintainer da auch langfristig Böcke drauf hat. Ich glaube, ich bleibe nach dem ganzen Lernaufwand jetzt erstmal bei Doctrine, da tut sich ja auch noch einiges (Obwohl ich stiller Fan der Criteria-API bin, und Doctrine mit Klammern im WHERE (kommi&#8230; komma&#8230; kommutativ? irgendwie schon) ist ja immer noch so eine Sache&#8230; Doctrine 1.2 zieht mit dem Symfony 1.3 Releasetermin gleich, und da gibt es auch wiederum ein paar tolle neue <a href="http://www.doctrine-project.org/upgrade/1_2">Features</a>. Übrigens ein Changelog mit sehr viel Stil ;)</p>The post <a href="https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/">Etwas untergegangen: Propel 1.4 is raus</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2009/11/12/etwas-untergegangen-propel-1-4-is-raus/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Doctrine  &#8211; Accessoren &#038; Mutatoren</title>
		<link>https://nerdpress.org/2009/11/07/doctrine-accessoren-mutatoren/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 07 Nov 2009 16:00:54 +0000</pubDate>
				<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Doctrine]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=520</guid>

					<description><![CDATA[<p>Also erstens, damit man die Doku versteht: Mutatoren sind natürlich &#8220;Setter&#8221; (setFirstname(string name)),  Accessoren &#8220;Getter&#8221; (getFirstname()). Doctrine ermöglicht es auf vielfältige Weise, Attribute eines OR-Objekts programmatisch zu erfragen bzw. zu verändern. Da jede Instanz von Doctrine_Record letztlich die abstrakte Elternklasse Doctrine_Access implementiert, wird der Zugriff und alle Änderungen durch die (magischen) PHP-Methoden __get(), __set() und __call() &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2009/11/07/doctrine-accessoren-mutatoren/" class="more-link">Continue reading<span class="screen-reader-text"> "Doctrine  &#8211; Accessoren &#038; Mutatoren"</span></a></p>
The post <a href="https://nerdpress.org/2009/11/07/doctrine-accessoren-mutatoren/">Doctrine  – Accessoren & Mutatoren</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Also erstens, damit man die Doku versteht: <a href="http://en.wikipedia.org/wiki/Mutator_method">Mutatoren</a> sind natürlich &#8220;Setter&#8221; (setFirstname(string name)),  <a href="http://en.wikipedia.org/wiki/Method_(computer_science)">Accessoren</a> &#8220;Getter&#8221; (getFirstname()). Doctrine ermöglicht es auf vielfältige Weise, Attribute eines OR-Objekts programmatisch zu erfragen bzw. zu verändern. Da jede Instanz von Doctrine_Record letztlich die abstrakte Elternklasse Doctrine_Access implementiert, wird der Zugriff und alle Änderungen durch die<a href="http://php.net/manual/de/language.oop5.magic.php"> (magischen) PHP-Methoden</a> __get(), __set() und __call() koordiniert. Zusätzlich bietet Doctrine eine Konfiguration, die es ermöglicht, jede Änderung an einem Objekt einem optionalen, zentralen Methodenaufruf zuzuleiten, der dann als eine Art <a href="http://de.wikipedia.org/wiki/Interceptor_(Entwurfsmuster)">Interzeptor</a> fungiert.</p>
<p><span id="more-520"></span>Doctrine_Access setzt unter anderem die Schnittstelle ArrayAccess voraus, die seit PHP 5 mit SPL existiert. Jedes Objekt, das diese Schnittstelle erbt, kann wie ein Array behandelt werden:</p>
<pre class="brush: php; title: ; notranslate">

$obj = new MyArray();

$obj&#x5B;&#039;property&#039;] = &quot;Value&quot;;

echo $obj&#x5B;&#039;property&#039;];

</pre>
<p>Was genau beim Zugriff auf das Objekt oder das Setzen seiner Eigenschaften via Array-Syntax + Zuweisungsoperator geschieht, bleibt dabei dem Entwickler überlassen, der das Verhalten durch die Implementierung der Methoden offsetSet(), offsetUnset(), offsetExists() und offsetGet() steuern kann.</p>
<p>Durch die php-internen magischen Methoden plus ArrayAccess ist es möglich, auf Attribute von Doctrine_Record Instanzen so zuzugreifen:</p>
<pre class="brush: php; title: ; notranslate">
echo $employee&#x5B;&#039;firstname&#039;];
</pre>
<p>oder so:</p>
<pre class="brush: php; title: ; notranslate">
echo $employee-&amp;amp;amp;amp;amp;gt;firstname;
</pre>
<p>oder auch so:</p>
<pre class="brush: php; title: ; notranslate">
echo $employee-&amp;amp;amp;amp;amp;gt;getFirstname();
</pre>
<p>Letztere Möglichkeit existiert nur im Symfony-Kontext, sprich wenn Doctrine als Symfony-Plugin läuft. Dann nämlich wird dem Model-Generator noch eine Symfony-Klasse &#8220;dazwischengeschoben&#8221;, nämlich sfDoctrineRecord. Diese Klasse kümmert sich um das Mapping von setXY() und getYZ()-Methoden auf die jeweiligen Accessoren und Mutatoren, die Doctrine bereitsstellt.</p>
<p>Möchte man nun eine dieser Methoden implementieren, also die &#8220;Magie&#8221; umgehen und seine &#8220;eigene&#8221; Methode namens setFirstname() implementieren, hat man ein Problem. Da die Sichtbarkeit nicht auf Getter beschränkt ist, könnte man auf die Idee kommen, statt $employee-&gt;setFirstname(&#8220;Kalle&#8221;) einfach $employee-&gt;firstname = &#8220;Kalle&#8221; zu schreiben. Um zu erreichen, dass jeder Mutator-Aufruf, egal welcher Syntax, auf die eigene, konkrete Methode setXYZ() umgeleitet wird, muss man nur eine Doctrine-Umgebungsvariable setzen:</p>
<pre class="brush: php; title: ; notranslate">
$manager = Doctrine_Manager::getInstance();
$manager-&amp;amp;amp;amp;amp;gt;setAttribute(&#039;auto_accessor_override&#039;, true);
</pre>
<p>Nun ist aber bei der Implementierung ein wenig Vorsicht geboten:</p>
<pre class="brush: php; title: ; notranslate">
function setFirstname($firstname){
if(empty($firstname))
{
$this-&amp;amp;amp;amp;amp;gt;firstname = &quot;Noname&quot;;
}
}
</pre>
<p>Damit hängen wir in einer Endlos-Schleife fest. Die Lösung ist, sofort den &#8220;Endpunkt&#8221; aller Mutator-Aufrufe anzuspringen, die Methode _set($attr, $value) (nicht zu verwechseln mit der magischen Methode __(set).</p>
<pre class="brush: php; title: ; notranslate">
function setFirstname($firstname){
if(empty($firstname))
{
$this-&amp;amp;amp;amp;amp;gt;_set(&#039;firstname&#039;, &#039;Noname&#039;);
}
}
</pre>
<p>So lässt sich jede Änderung am Objekt protokollieren und bei Bedarf triggern.</p>The post <a href="https://nerdpress.org/2009/11/07/doctrine-accessoren-mutatoren/">Doctrine  – Accessoren & Mutatoren</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Behave, baby!</title>
		<link>https://nerdpress.org/2009/10/16/behave-baby/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Fri, 16 Oct 2009 18:28:00 +0000</pubDate>
				<category><![CDATA[API]]></category>
		<category><![CDATA[Doctrine ORM]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Behaviours]]></category>
		<category><![CDATA[Doctrine]]></category>
		<category><![CDATA[Doctrine 1.1]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=422</guid>

					<description><![CDATA[<p>Doctrine macht es dem Entwickler leicht, seine Object-Models mit Businesslogic anzureichern. Entsprechende Methoden an der Doctrine_Record-  &#8211; oder allgemeiner &#8211; an einer entsprechenden Doctrine_Table-Kindklasse zu verdrahten ist ein Kinderspiel. Irgendwann trifft man dann auf einen Anwendungsfall, der eine entsprechende Zusatzfunktionalität erfordert, ohne dass das &#8220;Tätigkeitsfeld&#8221; dieser Funktionalität auf nur eine Gruppe von Entitäten zu begrenzen &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2009/10/16/behave-baby/" class="more-link">Continue reading<span class="screen-reader-text"> "Behave, baby!"</span></a></p>
The post <a href="https://nerdpress.org/2009/10/16/behave-baby/">Behave, baby!</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>Doctrine macht es dem Entwickler leicht, seine <a href="http://de.wikipedia.org/wiki/Object-Relational_Mapping">Object-Models</a> mit <a href="http://en.wikipedia.org/wiki/Business_logic">Businesslogic</a> anzureichern. Entsprechende Methoden an der Doctrine_Record-  &#8211; oder allgemeiner &#8211; an einer entsprechenden Doctrine_Table-Kindklasse zu verdrahten ist ein Kinderspiel. Irgendwann trifft man dann auf einen Anwendungsfall, der eine entsprechende Zusatzfunktionalität erfordert, ohne dass das &#8220;Tätigkeitsfeld&#8221; dieser Funktionalität auf nur eine Gruppe von Entitäten zu begrenzen wäre. Anstatt nun die immer gleichen Methoden für alle seine Object-Models, die die neue Funktionalität benötigen, zu implementieren und damit <a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">ziemlich viel Code zu produzieren</a>, möchte man lieber das ORM-Framework selbst erweitern. Auch hierfür bietet Doctrine die entsprechenden Schnittstellen: Einen Eventdispatcher zusammen mit ziemlich viele Stellen im Code, denen man &#8220;zuhören&#8221; kann und das Konzept der Behaviours (dt. etwa Verhaltensmuster): Oh behave!</p>
<p><span id="more-422"></span>Einige sogenannte <a href="http://www.doctrine-project.org/documentation/manual/1_1/en/behaviors:core-behaviors">Core-Behaviours</a> liefert Doctrine ab Werk bereits mit. Zu nennen sind da bspw. die komplexe Tree-Implementierung zur Verwaltung von Baumstrukturen der zugrundeliegenden (relationalen) Datenbank oder die etwas einfacherer Versionierung von Tupeln bis hin zum wirklich simplen, automatisch eingefügten Zeitstempel.</p>
<p>An der Bandbreite der zur Verfügung stehenden Core Behaviours kann man bereits sehen, dass man damit unterschiedlichste Dinge realisieren kann: Bspw. ist &#8220;Timestampable&#8221; ein reiner <a href="http://de.wikipedia.org/wiki/Ereignis_(Programmierung)">Event-Listener</a>, der die Events onSave bzw. onUpdate überwacht und jeweils automatisch die aktuelle Systemzeit mit dem entsprechenden Datensatz speichert.</p>
<p>Die Tree-Implementierung, als Beispiel sei NestedSets genannt, bietet dafür und im Gegensatz zum Timestampable-Verhaltensmuster einen Adapter, der  es dem Programmierer erlaubt, über Iteratoren und gewohnte Entwurfsmuster zum Zugriff auf hierarchische Datenstrukturen die zugrundeliegenden <a href="http://de.wikipedia.org/wiki/Recordset">RecordSets</a> zu bearbeiten.</p>
<h3>Ein neues Verhaltensmuster erstellen</h3>
<p>Anhand eines Beispiels möchte ich kurz zeigen, wie man ziemlich flott und ohne große Kenntnis der Doctrine-Architektur ein eigenes Verhalten zusammendengeln kann. Ich will dabei einerseits ein paar Automatismen via Event-Listener implementieren, andererseits aber auch zwei Helfermethoden schreiben, die mir häufig wiederkehrende Tasks abnehmen.</p>
<p>Die Anforderung ist, Pfade zu vom Benutzer hochgeladenen Dateien in einer <a href="http://de.wikipedia.org/wiki/Relation_(Datenbank)">Relation</a> abzulegen und auf Integrität zu prüfen. Vorhanden ist bereits eine Abstraktionsschicht für den Zugriff auf das Dateisystem, nennen wir die zuständige Klasse einmal File:</p>
<pre class="brush: php; title: ; notranslate">$file = new File(&#039;/pfad/zur/datei.jpg&#039;);</pre>
<p>Um die Funktionalität dieser Klassen mit unserem Doctrine Entities zu nutzen, entwerfen wir ein neues Verhaltensmuster als Ableitung der Klasse Doctrine_Template:</p>
<pre class="brush: php; title: ; notranslate">

class Doctrine_Template_File  extends Doctrine_Template
{
public function setFile(File $file)
{
}

public function getFile(File $file)
{
}
}

</pre>
<p>Jedes Template erhält einen Satz an Standardoptionen, die später in der Klassendefinition des Models selbst bzw. im <a href="http://de.wikipedia.org/wiki/YAML">Yaml-Schema</a> überschrieben werden können:</p>
<pre class="brush: php; title: ; notranslate">

class Doctrine_Template_File  extends Doctrine_Template
{

protected $_options = array(
&#039;name&#039;=&gt;&#039;pathname&#039;,
&#039;alias&#039;=&gt;null,
&#039;options&#039;=&gt;array(&#039;notnull&#039;=&gt;true),
&#039;checkExistence&#039; =&gt; true
);

public function setTableDefinition()
{
$name = $this-&gt;_options&#x5B;&#039;name&#039;];
if($this-&gt;_options&#x5B;&#039;alias&#039;])
{
$name . = &#039; as &#039; . $this-&gt;_options&#x5B;&#039;alias&#039;];
$this-&gt;hasColumn($name, &#039;string&#039;, 255, $this-&gt;_options&#x5B;&#039;options&#039;]);
}
}

// ...

</pre>
<p>Wir haben die Standardoptionen definiert und die Methode SetTableDefinition implementiert, welche unsere Optionen in eine Tabellendefinition aggregiert, also letztlich dafür sorgt, dass hinten ein <a href="http://de.wikipedia.org/wiki/Data_Definition_Language">DDL-Statement</a> herauskommt.</p>
<p>Nun kann man unser brandneues Verhaltensmuster schonmal in eine unserer Tabellen einbauen (Denkt euch die korrekten Indentations einfach dazu, der Codeformatter fluppt nicht so richtig):</p>
<pre class="brush: jscript; title: ; notranslate">

FileTuple:
actAs:
File:
name: filename
checkExistence: true

</pre>
<p>Um das ganze mit etwas Funktionalität anzureichern, implementieren wir anschließend unsere beiden Methoden getFile() und setFile():</p>
<pre class="brush: php; title: ; notranslate">

public function setFile(File $file)
{
return new File($this-&gt;_invoker&#x5B;$this-&gt;getOption(&#039;name&#039;)]);
}

public function getFile(File $file)
{
$this-&gt;_invoker&#x5B;$this-&gt;getOption(&#039;name&#039;)] = $file-&gt;getPathname();
}

</pre>
<p>Das ist schon die ganze Magie. Über $this-&gt;_invoker können wir auf die Instanz von Doctrine_Record zugreifen und dort all die lustigen Spielereien veranstalten, die uns Doctrine gönnt. Rufen wir nun auf einem unserer ObjectModel-Instanzen von FileTuple eine der neuen Methoden auf, so werden diese via __call() auf das Verhaltensmuster gemappt. Somit können wir mit</p>
<pre class="brush: php; title: ; notranslate">

Doctrine::getTable(&#039;FileTuple&#039;)-&gt;find(1)-&gt;getFile();

</pre>
<p>unsere File-Instanz zum einfachen Zugriff auf das Dateisystem erzeugen. Andersherum ist es möglich, mittels</p>
<pre class="brush: php; title: ; notranslate">

$file = new FileTuple();
$file-&gt;setFile(new File(&#039;/pfad/zur/datei.png&#039;));
$file-&gt;save();

</pre>
<p>ein neues RecordSet in unserer Datenbank zu erzeugen.</p>
<p>Als nächstes möchte ich, dass jede Instanz von Doctrine_Record, die mit unserem neuen Verhaltensmuster erzogen wurde, vor dem speichern prüft, ob der zu speichernde Dateipfad auch wirklich in unserem Dateisystem vorhanden ist. Diese Anforderung lösen wir, indem wir einen Eventlistener erzeugen und an unser Verhaltensmuster binden:</p>
<pre class="brush: php; title: ; notranslate">

public function setTableDefinition()
{
// ...
// ...

$this-&gt;addListener(new Doctrine_Template_Listener_File($this-&gt;_options));

}

</pre>
<p>Die Klasse Doctrine_Template_Listener_File erweitert Doctrine_Record_Listener, die wiederum das Interface Doctrine_Record_Listener_Interface implementiert. Dadurch, dass wir Doctrine_Record_Listener erweitern, müssen wir nicht selbst das komplette Interface herunterwurschteln, sondern brauchen nur die Callback-Methoden zu implementieren, die wir benötigen. In diesem Falle interessiert uns der Sprungpunkt &#8220;postValidate&#8221;:</p>
<pre class="brush: php; title: ; notranslate">

class Doctrine_Template_Listener_File extends Doctrine_Record_Listener
{
protected $_options;

public function __construct(array $options)
{
$this-&gt;_options = $options;
}

public function postValidate(Doctrine_Event $event)
{
if($this-&gt;_options&#x5B;&#039;checkExistence&#039;])
{
$file = $event-&gt;_invoker-&gt;getFile();
if(!$file-&gt;fileExists())
{
$error_stack = $event-&gt;getInvoker()-&gt;getErrorStack();
$error_stack-&gt;add($this-&gt;_options&#x5B;&#039;name&#039;], &#039;The file does not exist.&#039;);
}
}
}
}

</pre>
<p>Wieder holen wir mit $event-&gt;_invoker die jeweilige Instanz von Doctrine_Record, besorgen und über unser Behaviour via getFile() unser File-Objekt und können nun testen, ob die Datei tatsächlich im Dateisystem existiert:</p>
<pre class="brush: php; title: ; notranslate">

$file = new FileTuple();
$file-&gt;setFile(&#039;/pfad/der/nicht/existiert&#039;);

// Exception, Validation error!
$file-&gt;save();

</pre>
<p>Das war nur ein kitzekleiner Einblick in das, was mit Behaviours innerhalb der Doctrine-Welt grundsätzlich machbar ist. Einen weiterführenden Artikel gibt es im Doctrine-Kochbuch unter <a href="http://www.doctrine-project.org/blog/cookbook-recipe-relation-dql-behavior">http://doctrine-project.org/blog/cookbook-recipe-relation-dql-behavior</a>. Ansonsten hilft einem zusätzlich ein längerer Blick auf den Code der Core-Behaviours. In Punkto Code-Style sollte man beachten, dass die Doctrine-Welt den Pear/Zend-Coding Guidelines folgt, so enthält jede Klasse grundsätzlich ihren kompletten Namensraum, und die Datei ist jeweils in einem Unterverzeichnis angesiedelt, das ähnlich wie in der Java-Welt den jeweiligen Namespace bildet. In Hinblick auf Abwärtskompatibilität zu php5.3 sollte man das auch so beibehalten. Auf die Ablage der Behaviour-Klassen im Dateisystem und das Autoloading (um das man sich nur kümmern muss, wenn man sich mit Doctrine außerhalb von Symfony bewegt) gehe ich hier nicht gesondert ein.</p>
<p>Zuletzt ist zu sagen, dass das obige Beispiel durchaus einen konkreten Anwendungsfall beschreibt: Ich habe ein ähnliches Verhaltensmuster mit einem meiner Symfony-Plugins gebundlet, dem <a href="http://www.symfony-project.org/plugins/sfFilebasePlugin">sfFilebasePlugin</a>. Hier kann Frau &#8211; so sie denn möchte &#8211; auch einmal reinschauen.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 1749px; width: 1px; height: 1px;">/**<br />
* Set table definition for Timestampable behavior<br />
*<br />
* @return void<br />
*/<br />
public function setTableDefinition()<br />
{<br />
$name = $this-&gt;_options[&#8216;name&#8217;];<br />
if ($this-&gt;_options[&#8216;alias&#8217;])<br />
{<br />
$name .= &#8216; as &#8216; . $this-&gt;_options[&#8216;alias&#8217;];<br />
}<br />
$this-&gt;hasColumn($name, &#8216;string&#8217;, 255, $this-&gt;_options[&#8216;options&#8217;]);<br />
$this-&gt;addListener(new Doctrine_Template_Listener_File($this-&gt;_options));<br />
}</div>The post <a href="https://nerdpress.org/2009/10/16/behave-baby/">Behave, baby!</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
