<?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>PHP hates me - Der PHP Blog &#187; Datenbanken</title>
	<atom:link href="http://www.phphatesme.com/archives/category/mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.phphatesme.com</link>
	<description>PhpHatesMe, but that&#039;s ok!</description>
	<lastBuildDate>Tue, 07 Feb 2012 06:00:42 +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>05.12. Doctrine 2</title>
		<link>http://www.phphatesme.com/blog/mysql/05-12-doctrine-2/</link>
		<comments>http://www.phphatesme.com/blog/mysql/05-12-doctrine-2/#comments</comments>
		<pubDate>Mon, 05 Dec 2011 00:00:00 +0000</pubDate>
		<dc:creator>Mayflower Blog</dc:creator>
				<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[phmnetwork]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.mayflower.de/archives/792-guid.html</guid>
		<description><![CDATA[
    
    .serendipity_entry h1 {
        font-size: 20px;
        margin: 13px 0;
    }

    .serendipity_entry h2 {
        margin: 7px 0;
    }

    .serendipity_entry ul {
        padding-left: 3em;
    }

    .gist-data {
        max-height: 250px...]]></description>
			<content:encoded><![CDATA[
    <style>
    .serendipity_entry h1 {
        font-size: 20px;
        margin: 13px 0;
    }

    .serendipity_entry h2 {
        margin: 7px 0;
    }

    .serendipity_entry ul {
        padding-left: 3em;
    }

    .gist-data {
        max-height: 250px;
        overflow: auto;
    }
</style>


<h1>Introduction</h1>
<p>
    <i>Object-relational mapping</i> (ORM) frameworks have been around for several years now and for some people, ORM is
    already outdated by now. As we have seen with other technologies and concepts before, PHP is not exactly what we call
    an early adopter among the programming languages. Thus it took some time for ORM to grow up in the PHP context.
</p>

<p>
    There have been some frameworks before <i>Doctrine 2</i> that implement ORM (remember e.g. <a href="http://propelorm.org" title="Propel">Propel</a>) specific tasks but most
    of them lack the required maturity to be used in large projects. With <i>Doctrine 2</i>, PHP takes a huge step into the right
    direction  <i>Doctrine 2</i> is fast, extensible and easy to use.
</p>

<p>
    This article will take you on a tour through the main concepts of <i>Doctrine 2</i> in the first part and then explain how
    to use it in a real world application in the second part. Since at the time of writing <i>Zend Framework</i> 1.11.xx (<i>ZF</i>) is
    very popular, we will integrate <i>Doctrine 2</i> into a <i>ZF</i> project.
</p>
<br />
        
<h1>Basic Concepts</h1>
<p>
    To understand <i>Doctrine 2</i>, we have to take a look at some relevant terms (or in this case objects), study their behavior
    and practice their usage. We start with some introductory phrases on ORM systems and then go on to the concepts underlying
    <i>Doctrine 2</i>: <i>Entity Objects</i>, the <i>Entity Manager</i>, <i>Repositories</i> and <i>Proxies</i>.
</p>
<br />
<h2>Object-relational Mapping</h2>
<p>
    Since the beginning of Object-Orientation, people had to manage the persistence of their application's state resp.
    their objects. In the context of Web Application Development, this usually involves a Database server which is being
    consulted using a Query Language. One example for this pattern is a PHP application that uses some kind of SQL server
    by sending SQL queries to it. Another one is an application using a CouchDB server by querying it via its REST API. <br />
    Due to the author's laziness, we will talk in terms of relational databases from now on. Keep in mind, that you can
    accomplish almost everything mentioned here with NoSQL databases, too.
</p>
<p>
    ORM relates value objects that exist in an application's business logic to database records.
    Thus every object that should be persistent is saved in one row of a database table. The most common approach is to
    map classes to tables and the classes' objects to rows in the these tables.<br />
    Besides writing objects to a database, ORM systems are also intended to ease the process of finding data stored in the
    database. When talking in terms of ORM, finding data always means making the framework fetch one or many objects
    that meet a certain criteria.
</p>

<br />
<h2>Entity Objects</h2>
<p>
    The objects that are being managed by an ORM system are called <i>Entity Objects</i>. Every entity object relates to one
    entry in a table. In <i>Doctrine 2</i>, the classes that represent entities do not have to fulfill special requirements
    like inheriting from a certain super class (as you might have seen in other database abstraction frameworks like
    <i>Zend_Db</i>). When creating a new entity class with <i>Doctrine 2</i>, all you have to do is to write down a regular PHP class
    with properties. Besides this, you have to provide some hints on how these attributes should be persisted.
    The information how entity attributes relate to columns in the DB is called Metadata. Metadata can be described in
    different ways: By default there are metadata drivers for descriptions in XML, YAML and PHP. The fourth
    and most popular driver is based on DocBlock annotations (since in PHP, annotations aren't a language feature as in
    Java (see <a href="http://en.wikipedia.org/wiki/Java_annotation">Wikipedia</a>), they are contained by the classes' and attributes' DocBlocks).
    We will use annotations to describe our entities metadata. To get an impression on how easy this is,
    take a look at the following example.
</p>

<br />
<script src="https://gist.github.com/1430553.js?file=gistfile1.aw"></script>
<br />

<p>
    This example contains all it needs to tell <i>Doctrine 2</i> about the new entity <code>User</code>. With this class, you can create,
    find, delete and modify user objects and persist their state to the underlying database. But keep in mind: as long
    as you don't need any persistence features, you can use your user objects just like any other objects!
</p>
<p>
    The next two objects resp. object types we will describe are responsible for doing the ORM functionality:
    persisting and finding.
</p>

<br />
<h2>The Entity Manager</h2>
<p>
    To use ORM functionality, the <i>Entity Manager</i> (<code>Doctrine\ORM\EntityManager</code>) is the main access point to <i>Doctrine 2</i>. The entity manager is
    responsible  as you might have guessed  for managing entities and for building a facade for the whole framework.
    To accomplish its tasks, the entity manager uses some helpers. The <i>Unit of Work</i> object for example collects entities
    that should be written back to the database and is capable of doing this in batches. This way, database operation
    can be executed with almost no overhead and therefore are really fast.
</p>
<p>
    Another dependency of the entity manager is the <i>Event Manager</i>. To be as extensible as possible, <i>Doctrine 2</i> comes
    with an event system that publishes all important state changes to the outside as events. You can register for such
    events and extend the life cycle of your entity objects at one single point.
</p>
<p>
    The entity manager's <a href="http://www.doctrine-project.org/api/orm/2.0/doctrine/orm/entitymanager.html">API</a> combines methods for managing entities (<code>find</code>, <code>persist</code>, <code>contains</code>, <code>copy</code>, <code>detatch</code>, <code>merge</code>,
    <code>remove</code> and <code>refresh</code>), methods that control the use of transations (<code>beginTransaction</code>, <code>commit</code>, <code>flush</code>, <code>rollback</code> and
    <code>transactional</code>) and some helper methods for creating custom queries and accessing some of the entity manager's dependencies.
</p>
<p>
    The following example shows how to query an object from the entity manager, modify it and write the changes back into
    the database.
</p>

<br />
<script src="https://gist.github.com/1430549.js?file=gistfile1.aw"></script>
<br />

<p>
    Creating a new persistent object is almost as easy as modifying it:
</p>

<br />
<script src="https://gist.github.com/1430548.js?file=gistfile1.aw"></script>
<br />

<br />
<h2>Repositories</h2>
<p>
    For finding entities, <i>Repositories</i> are used. Every entity class has its own repository which is responsible for
    finding entities of that type. By default, repositories have some handy methods for fetching entities that match certain criteria:
    <ul>
        <li><code>find</code>: Finds an entity by its primary key / identifier</li>
        <li><code>findAll</code>: Finds all entities of the repository's entity type</li>
        <li>
            <code>findBy</code> / <code>findOneBy</code>: Finds all resp. one entity that matches the passed criteria:<br />
            <script src="https://gist.github.com/1430546.js?file=gistfile1.txt"></script>
        </li>
        <li>
            <code>findBy&lt;attribute&gt;</code> / <code>findOneBy&lt;attribute&gt;</code>: Magic methods that ease the filtering by a single attribute:<br />
            <script src="https://gist.github.com/1430544.js?file=gistfile1.aw"></script>
        </li>
    </ul>
</p>

<p>
    To access a repository, all you have to do is ask the entity manager for one. If you have implemented your own
    repository, it will be returned by <code>Doctrine\ORM\EntityManager::getRepository()</code>. Otherwise, <i>Doctrine 2</i> will provide
    a generic repository. The main reason to implement custom repository classes is to group custom queries for an entity
    type to make them reusable. For custom query logic, there are several mechanisms you can use: You can either use
    Doctrine's query builder that implements an API similar to <i>Zend_Db_Select</i> or queries written in the <i>Doctrine Query
    Language</i> (<i>DQL</i>) or you can even execute plain SQL queries. With these options, it is also possible to migrate old
    applications which use complex queries by just wrapping these queries into the methods of custom repositories.
</p>

<br />
<h2>Proxies</h2>
<p>
    When traversing a graph of entity objects (which is required when entities are having relations to other entities),
    it would be very expensive (in the sense of requiring many database queries) to fetch every depending entity with
    an additional query. Therefore <i>Doctrine 2</i> uses the concept of <i>Proxy</i> objects that represent regular entity objects
    which have not been populated with data from the database. Take a look at the following example where the entity
    <code>Group</code> aggregates a list of <code>User</code> objects in its member property. When accessing the members list, <i>Doctrine 2</i> provides a
    collection of proxy objects instead of complete <code>User</code> objects. When an object of this collection is being asked for
    one of its properties, Doctrine loads the object's data from the database. This way, the users' data is not loaded
    until it is really needed.
</p>
<br />
<script src="https://gist.github.com/1430542.js?file=gistfile1.aw"></script>
<br />
<br />

<h1>Advanced Mapping Concepts</h1>
<p>
    This section describes some advanced concepts that are required when mapping entity classes that have relationships to
    other entity classes. Possible relationship types are association and inheritance. Inheritance is the mechanism used for
    representing subtypes in object-oriented programming languages. An example would be a class <code>User</code> that implements
    methods every user of a software should have and a class <code>Administrator</code> inheriting from <code>User</code> that adds methods for
    determining the administrator's access rights.
</p>
<p>
    Association is a weaker relation type. It means that an entity object can be related to other entity objects of other
    types. In terms of relational databases, there are three types of association which differ in the number of entities
    an object is related to: 1:1, 1:n and n:m relationships. n and m are placeholders and mean multiple.
</p>

<br />
<h2>Association</h2>
<p>
    To put objects of an entity type into relation, you just have to mention this relation in the entity class' mapping
    information. The simplest case is a unidirectional 1:1 relationship. In the following example we describe a <code>User</code> entity
    which has its access information (user credentials) encapsulated into another entity class called <code>UserCredential</code>. Since
    every user has at most one credential object and every credential object may only be associated to one user object,
    this is a 1:1 relationship.
</p>

<br />
<script src="https://gist.github.com/1430540.js?file=gistfile1.aw"></script>
<br />

<p>
    If the relationship should be bidirectional, include the <code>OneToOne</code> attribute in the other class, too, and add an
    attribute which denotes the attribute of the other entity that mapps the related object:
</p>
<br />
<script src="https://gist.github.com/1430535.js?file=gistfile1.aw"></script>
<br />
<p>
    This way, you can access the user object from the credentials object, too.<br \ />
    Most of the times, developers have to deal with relationships which include many objects on at least one side.
    These relationships are called 1:n or n:m relationships. This means that either one or multiple entities are standing
    in relationship with an arbitrary number of entities of another type. To accomplish this, you have to use the mapping
    keywords <code>OneToMany</code> or <code>ManyToMany</code> when describing your entities. Besides that, the mapping works the exact same way as
    with 1:1 relationships.
</p>
<p>
    There are however some tricks you should know when dealing with collections of associated entity objects. Consider
    the following relationship between the entity classes <code>User</code> and <code>Group</code>:
</p>

<br />
<script src="https://gist.github.com/1430534.js?file=gistfile1.txt"></script>
<br />

<p>
    When a group has at least one member, the group object will have a collection of the type
    <code>Doctrine\Common\Collections\ArrayCollection</code> set as its members property. This collection contains
    all user objects (or proxy objects as we have seen before) and can be modified intuitively with the methods <code>add</code> and
    <code>removeElement</code>. To honor object-orientation, you might want to introduce custom methods for these tasks. If you do so,
    you get into trouble when the group object does not have any users associated. In this case, the collection will simply be
    set to null. To avoid checks whether the collection has already be initialized, you should to this by yourself
    in the entity class' constructor:
</p>

<br />
<script src="https://gist.github.com/1430532.js?file=gistfile1.aw"></script>
<br />

<p>
    It is also important to notice that one entity has to update the other entity's state as well when a relationship
    between to objects is created or removed. Take care to do this only in one class to avoid endless recursion loops!
    This class is called the <i>Owning Side</i> of the relationship. When implementing a bidirectional relationship, the other
    class is called the <i>Inverse Side</i>. It is important to determine owning and inverse side and implement the the classes
    accordingly to avoid greater trouble during debugging.
</p>
<p>
    There are some more features implemented by <i>Doctrine 2</i> enabling developers to specify their entities' relationships
    including sorting, pre-fetching and indexing. These topics are not covered in this article but are explained very
    understandable in the <a href="http://www.doctrine-project.org/docs/orm/2.1/en/reference/association-mapping.html"><i>Doctrine 2</i> documentation</a>.
</p>

<br />
<h2>Inheritance</h2>
<p>
    Subtyping can be implemented in different ways using <i>Doctrine 2</i>. The main difference between these implementations
    is how the inheritance is mapped to the database. The options are to have one table for every class (<i>Class Table Inheritance</i>),
    to have one table for all classes in a hierarchy (<i>Single Table Inheritance</i>) and to have a table for every specialized
    sub-class of a given super-class (<i>Mapped Super Class</i>).We will give a short overview on all three alternatives, you have
    to pick the right one yourself. This decision should be made based on how many common attributes there are in your sub-classes.
</p>

<h3>Mapped Superclasses</h3>
<p>
    Introducing a mapped superclass is probably the easiest way for specifying inheritance but might lead to
    many duplicate columns in your database schema. The superclass of your entities is not being declared as an entity
    itself (and might also be declared abstract) but provides attributes and optionally methods that will be available
    in all subclasses. When creating the database schema, <i>Doctrine 2</i> merges all attributes and relationships of the
    superclass into the definitions of the subclasses and processes them as regular entities.
</p>

<br />
<script src="https://gist.github.com/1430528.js?file=gistfile1.aw"></script>
<br />

<p>
    After creating the database from this mapping information, your tables will look like this: <br />
</p>

<img src="http://blog.mayflower.de/uploads/PaulBlog/User-AdministratorusingaMappedSuperclass.png" />

<h3>Single Table Inheritance</h3>
<p>
    When having entities that are very similar besides some few attributes, you might want to store them together in one
    database table. This approach is called <i>Single Table Inheritance</i>. To distinguish between the different types, there is
    always a column marked as discriminator column and a discriminator map that tells <i>Doctrine 2</i> which values in the
    discriminator indicate what entity types.
</p>

<br />
<script src="https://gist.github.com/1430523.js?file=gistfile1.txt"></script>
<br />

<p>
    These definitions cause the existing of one single table called User with all the attributes declared inside the
    classes User and Administrator plus a column type  the discriminator column. When working with entities of these
    types, Doctrine will manage the type flag automatically for you.
</p>
<p>
    The resulting database schema looks as illustrated by the following diagram:<br />
</p>

<img src="http://blog.mayflower.de/uploads/PaulBlog/User-AdministratorusingSingleTableInheritance.png" />

<h3>Class Table Inheritance</h3>
<p>
    Having each entity type stored in its own table is always good for keeping your schema extensible. When you have to
    create a new subtype, <i>Doctrine 2</i> will just create a new table for this type and it can inherit the logic and common
    attributes of a superclass. The only overhead you have with this approach is that all tables that correspond to subtypes
    have to maintain a relationship to their supertype's table. Using class table inheritance, the example with the entities
    User and Administrator looks like this:
</p>

<br />
<script src="https://gist.github.com/1430465.js?file=gistfile1.aw"></script>
<br />

<p>
    Besides the inheritance type, there is no difference to the example using single table inheritance. The outcome on
    the resulting database scheme is huge. Now you have to separate tables which store users and administrators. Every
    record in the table Administrator has a corresponding record in the User table.
</p>

<img src="http://blog.mayflower.de/uploads/PaulBlog/User-Administratorusingclasstableinheritance.png" />

<hr />

<p>
    This was the first part of this article. Stay tuned for part II which will be published tomorrow (on 6th of December 2011)! In the second part, we will integrate <i>Doctrine 2</i> into a <i>Zend Framework</i> application and include a generic sandbox (ZF-)project with <i>Doctrine 2</i>!
</p> 
    <img src="http://feeds.feedburner.com/~r/MayflowerPHP/~4/h_v7m4QOD2U" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/05-12-doctrine-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Info über MySQL Zertifizierungen?</title>
		<link>http://www.phphatesme.com/blog/allgemein/info-uber-mysql-zertifizierungen/</link>
		<comments>http://www.phphatesme.com/blog/allgemein/info-uber-mysql-zertifizierungen/#comments</comments>
		<pubDate>Wed, 02 Nov 2011 08:59:29 +0000</pubDate>
		<dc:creator>PHP Gangsta</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[phmnetwork]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.phpgangsta.de/?p=3835</guid>
		<description><![CDATA[Ich plane schon länger eine weitere Zertifizierung zu machen, nachdem die Zend Certified Engineer Prüfung letztes Jahr so gut geklappt hat. Es gibt auch einige Zertifikate die mich interessieren würden: ZCE PHP 5.3 &#8211; wäre ein schönes Update zu meinem ZCE PHP 5, für &#060; 75 Euro würd ich drüber nachdenken, aber 195 Euro ist [...]<br /><br /> Ähnliche Artikel:<ol><li><a href='http://www.phpgangsta.de/praxis-workshop-php-basics-web-basierte-systeme-mit-php-und-mysql' rel='bookmark' title='Praxis-Workshop PHP Basics: Web-basierte Systeme mit PHP und MySQL'>Praxis-Workshop PHP Basics: Web-basierte Systeme mit PHP und MySQL</a></li><li><a href='http://www.phpgangsta.de/zend-certified-engineer' rel='bookmark' title='Zend Certified Engineer'>Zend Certified Engineer</a></li><li><a href='http://www.phpgangsta.de/zce_online_praxis_tests' rel='bookmark' title='ZCE Online Praxis Tests'>ZCE Online Praxis Tests</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>Ich plane schon länger eine weitere Zertifizierung zu machen, nachdem die <a href="http://www.phpgangsta.de/tag/zce" >Zend Certified Engineer</a> Prüfung letztes Jahr so gut geklappt hat. Es gibt auch einige Zertifikate die mich interessieren würden:</p>
<ul>
<li><a href="http://shop.zend.com/de/php-certification/zend-php5-certification-voucher.html" >ZCE PHP 5.3</a> &#8211; wäre ein schönes Update zu meinem ZCE PHP 5, für &lt; 75 Euro würd ich drüber nachdenken, aber 195 Euro ist mir für das Update doch etwas zu viel</li>
<li>ZCE-ZF die <a href="http://shop.zend.com/de/zf-certification/zend-framework-certification-voucher.html" >Zend Framework Zertifizierung</a>. Da ich täglich mit dem Zend Framework arbeite ist das schon länger auf meiner Liste, aber da in wenigen Monaten (Schätzung von Matthew Weier O&#8217;Phinney ist April 2012) Version 2.0 erscheinen wird wollte ich noch darauf warten, sonst stehe ich bald vor dem selben Problem wie oben. Da gedulde ich mich noch ein wenig.</li>
<li><a href="http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=332" >Oracle Certified Professional, MySQL 5 Developer</a>. Besteht aus 2 Prüfungen die jeweils 154 Euro kosten. Mit MySQL hat man auch fast jeden Tag zu tun.</li>
</ul>
<p>Ich habe auf der Suche danach zwei Dinge gefunden: Eine 5 Tage <a href="http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getCourseDesc?dc=D61830GC10" >MySQL for Developers</a> Schulung für 2200 Euro und ein <a href="http://www.amazon.de/gp/product/0672328127/ref=as_li_ss_tl?ie=UTF8&amp;tag=warha40kgalle-21&amp;linkCode=as2&amp;camp=1638&amp;creative=19454&amp;creativeASIN=0672328127" >Certification Study Guide</a> für 44 Euro.</p>
<p>Gibt es noch weitere interessante Zertifizierungen für einen Webentwickler die sich lohnen?</p>
<p>Hat jemand von euch schon die MySQL Prüfungen gemacht? Hat jemand von euch eventuell Lernmaterial, beispielsweise das oben genannte Buch günstig zu verkaufen?</p>
<p><br/><br/>
<p>Ähnliche Artikel:</p>
<ol>
<li><a href='http://www.phpgangsta.de/praxis-workshop-php-basics-web-basierte-systeme-mit-php-und-mysql' rel='bookmark' title='Praxis-Workshop PHP Basics: Web-basierte Systeme mit PHP und MySQL'>Praxis-Workshop PHP Basics: Web-basierte Systeme mit PHP und MySQL</a></li>
<li><a href='http://www.phpgangsta.de/zend-certified-engineer' rel='bookmark' title='Zend Certified Engineer'>Zend Certified Engineer</a></li>
<li><a href='http://www.phpgangsta.de/zce_online_praxis_tests' rel='bookmark' title='ZCE Online Praxis Tests'>ZCE Online Praxis Tests</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/allgemein/info-uber-mysql-zertifizierungen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTTP-Authentifizierung gegen MySQL</title>
		<link>http://www.phphatesme.com/blog/mysql/http-authentifizierung-gegen-mysql/</link>
		<comments>http://www.phphatesme.com/blog/mysql/http-authentifizierung-gegen-mysql/#comments</comments>
		<pubDate>Mon, 10 Oct 2011 04:00:20 +0000</pubDate>
		<dc:creator>ebene7</dc:creator>
				<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[phmnetwork]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=3025</guid>
		<description><![CDATA[Die Technik der HTTP-Authentifizierung ist sicherlich für die meisten ein alter Hut. Benutzer und Password in eine .htpasswd schreiben, .htaccess ins Verzeichnis und schon kommt nicht mehr jeder an die Bilder der letzten Party dran. Letztens habe ich wieder etwas nettes entdeckt, das ich vorher nicht kannte (allerdings auch nicht brauchte). Statt der .htpasswd können [...]]]></description>
			<content:encoded><![CDATA[<p>Die Technik der <a href="http://de.wikipedia.org/wiki/HTTP-Authentifizierung" >HTTP-Authentifizierung</a> ist sicherlich für die meisten ein alter Hut. Benutzer und Password in eine .htpasswd schreiben, .htaccess ins Verzeichnis und schon kommt nicht mehr jeder an die Bilder der letzten Party dran. Letztens habe ich wieder etwas nettes entdeckt, das ich vorher nicht kannte (allerdings auch nicht brauchte). <span id="more-3025"></span></p>
<p>Statt der .htpasswd können die Benutzerdaten auch in anderen Datenquellen stehen, z.B. einer MySQL Datenbank. Das kann dann sinnvoll sein, wenn man SVN und einen Bugtracker (z.B. Mantis, Redmine, Bugzilla oder auch Jira etc.) im Einsatz hat. In dem Fall kann dann die SVN-Benutzerverwaltung über die Daten des Bugtrackers abgedeckt werden.</p>
<p>Über verschiedene Module lassen sich wohl auch noch andere Datenquellen dafür nutzen. Bevor ich nun aber groß was dazu schreibe, verweise ich lieber auf <a href="http://www.sousgarden.com/2009/04/18/apache2-mit-mysql-authentifizierung/" >SousGarden</a>, denn dort ist es schon sehr anschaulich beschrieben. Eine Suchmaschine eurer Wahl bringt aber auch genug andere Treffer zu &#8220;mod_auth_mysql&#8221; oder &#8220;mod_auth_*&#8221;, wenn das nicht reicht oder eine andere Datenbank verwendet werden soll.</p>
<p>Weitere Information zur Verwendung finden sich auch noch <a href="http://modauthmysql.sourceforge.net/CONFIGURE" >hier</a>. Sämtliche Anpassungen an der Apache-Konfiguration lassen sich auch in der .htaccess machen. Nun viel Spaß beim Probieren! Erfahrungsberichte bitte wie immer in den Kommentaren hinterlassen <img src='http://blog.ebene7.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>


<!-- Begin SexyBookmarks Menu Code -->
<div class="sexy-bookmarks sexy-bookmarks-expand sexy-bookmarks-center sexy-bookmarks-bg-caring-old">
<ul class="socials">
		<li class="sexy-comfeed">
			<a href="http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/feed" rel="nofollow" class="external" title="Abonniere die Kommentare für diesen Beitrag">Abonniere die Kommentare für diesen Beitrag</a>
		</li>
		<li class="sexy-twitter">
			<a href="http://twitter.com/home?status=HTTP-Authentifizierung+gegen+MySQL+-+UNKNOWN_ERROR+&amp;source=shareaholic" rel="nofollow" class="external" title="Twittere diesen Artikel!">Twittere diesen Artikel!</a>
		</li>
		<li class="sexy-facebook">
			<a href="http://www.facebook.com/share.php?v=4&amp;src=bm&amp;u=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;t=HTTP-Authentifizierung+gegen+MySQL" rel="nofollow" class="external" title="Empfehle diesen Artikel bei Facebook">Empfehle diesen Artikel bei Facebook</a>
		</li>
		<li class="sexy-misterwong">
			<a href="http://www.mister-wong.de/addurl/?bm_url=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;bm_description=HTTP-Authentifizierung+gegen+MySQL&amp;plugin=sexybookmarks" rel="nofollow" class="external" title="Füge hinzu zu Mister Wong">Füge hinzu zu Mister Wong</a>
		</li>
		<li class="sexy-delicious">
			<a href="http://delicious.com/post?url=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;title=HTTP-Authentifizierung+gegen+MySQL" rel="nofollow" class="external" title="Empfehle diesen Artikel bei del.icio.us">Empfehle diesen Artikel bei del.icio.us</a>
		</li>
		<li class="sexy-technorati">
			<a href="http://technorati.com/faves?add=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/" rel="nofollow" class="external" title="Empfehle diesen Artikel bei Technorati">Empfehle diesen Artikel bei Technorati</a>
		</li>
		<li class="sexy-stumbleupon">
			<a href="http://www.stumbleupon.com/submit?url=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;title=HTTP-Authentifizierung+gegen+MySQL" rel="nofollow" class="external" title="Bei StumbleUpon einstellen">Bei StumbleUpon einstellen</a>
		</li>
		<li class="sexy-digg">
			<a href="http://digg.com/submit?phase=2&amp;url=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;title=HTTP-Authentifizierung+gegen+MySQL" rel="nofollow" class="external" title="Digg this!">Digg this!</a>
		</li>
		<li class="sexy-myspace">
			<a href="http://www.myspace.com/Modules/PostTo/Pages/?u=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;t=HTTP-Authentifizierung+gegen+MySQL" rel="nofollow" class="external" title="Sende dies zu MySpace">Sende dies zu MySpace</a>
		</li>
		<li class="sexy-linkedin">
			<a href="http://www.linkedin.com/shareArticle?mini=true&amp;url=http://blog.ebene7.com/2011/10/10/http-authentifizierung-gegen-mysql/&amp;title=HTTP-Authentifizierung+gegen+MySQL&amp;summary=Die%20Technik%20der%20HTTP-Authentifizierung%20ist%20sicherlich%20f%C3%BCr%20die%20meisten%20ein%20alter%20Hut.%20Benutzer%20und%20Password%20in%20eine%20.htpasswd%20schreiben%2C%20.htaccess%20ins%20Verzeichnis%20und%20schon%20kommt%20nicht%20mehr%20jeder%20an%20die%20Bilder%20der%20letzten%20Party%20dran.%20Letztens%20habe%20ich%20wieder%20etwas%20nettes%20entdeckt%2C%20das%20ich%20vorher%20nic&amp;source=ebene7" rel="nofollow" class="external" title="Empfehle diesen Artikel bei LinkedIn">Empfehle diesen Artikel bei LinkedIn</a>
		</li>
</ul>
<div style="clear:both;"></div>
</div>
<!-- End SexyBookmarks Menu Code -->

]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/http-authentifizierung-gegen-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL-Functions vs. PDO vs. Doctrine2</title>
		<link>http://www.phphatesme.com/blog/mysql/mysql-functions-vs-pdo-vs-doctrine2/</link>
		<comments>http://www.phphatesme.com/blog/mysql/mysql-functions-vs-pdo-vs-doctrine2/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 14:56:36 +0000</pubDate>
		<dc:creator>Gjero Krsteski</dc:creator>
				<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[phmnetwork]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://krsteski.de/?p=637</guid>
		<description><![CDATA[Neulich bin ich in ein komplett neues Projekt eingestiegen. Das Team diskutierte über ein mögliches ORM Framework. Die Anforderungen an da ORM-Framework waren: „Wir möchten keine SQL-Statement mehr schreiben“, „Wir möchten nur mit Objekten arbeiten“, „Ein Scaffolding oder ein Reverse-Engineering wäre super, ist aber kein muss“ und „Wir möchten Vor- und Nachteile von Doctrine2 gegenüber [...]]]></description>
			<content:encoded><![CDATA[<p>Neulich bin ich in ein komplett neues Projekt eingestiegen. Das Team diskutierte über ein mögliches ORM Framework. Die Anforderungen an da ORM-Framework waren: „Wir möchten keine SQL-Statement mehr schreiben“, „Wir möchten nur mit Objekten arbeiten“, „Ein Scaffolding oder ein Reverse-Engineering wäre super, ist aber kein muss“ und „Wir möchten Vor- und Nachteile von Doctrine2 gegenüber MySQL-Functions und PDO haben“. Zuletzt auch noch diese: „Wenn möglich, dann soll es mit nicht relationalen Datenbanken sowie mit relationalen Datenbanken zusammen arbeiten können“.<span id="more-637"></span></p>
<p>Für dieses Mamut-Benchmarking habe ich die TestSuite von Roman Borschel <a href="http://code.google.com/p/php-orm-benchmark/source/browse/#svn/trunk/doctrine_2" >[Benchmark of the major PHP ORMs]</a> verwendet. Die TestSuite ist auch eine Art Benchmarking verschiedener ORM-Frameworks. Daraus habe ich mir jeweils das Benchmark für Doctrine2 und PDO rausgezogen und die Tests für meine Testzwecke erweitert. Anschließend habe ich ein neues Benchmark mit den nativen MySQL-Funktionen hinzugefügt. Das PDO- und das MySQL- Benchmark wurden als Active-Record implementiert.</p>
<p>Als Datenbank habe ich MySQL verwendet. Dort habe ich zwei Tabellen „Author“ und „Book“ mit einer 1:n Beziehung, Fremdschlüssel und Referenz-Optionen erstellt. Also ein Autor kann viele Bücher haben. Hier die Tabellendefinition und die SQL-Statements:</p>
<pre name="code" class="php">

Author
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstName | varchar(128) | NO   |     | NULL    |                |
| lastName  | varchar(128) | NO   |     | NULL    |                |
| email     | varchar(128) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+

Book
+-----------+---------------+------+-----+---------+----------------+
| Field     | Type          | Null | Key | Default | Extra          |
+-----------+---------------+------+-----+---------+----------------+
| id        | int(11)       | NO   | PRI | NULL    | auto_increment |
| author_id | int(11)       | YES  | MUL | NULL    |                |
| title     | varchar(255)  | NO   |     | NULL    |                |
| isbn      | varchar(24)   | NO   |     | NULL    |                |
| price     | decimal(10,0) | NO   |     | NULL    |                |
+-----------+---------------+------+-----+---------+----------------+
</pre>
<p>
Hier die CREATE TABLE Statements, die von Doctrine2 generiert werden.<br />
</p>
<pre name="code" class="php">
CREATE TABLE `Author` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `firstName` varchar(128) NOT NULL,
  `lastName` varchar(128) NOT NULL,
  `email` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1701 DEFAULT CHARSET=utf8;

CREATE TABLE `Book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `author_id` int(11) DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `isbn` varchar(24) NOT NULL,
  `price` decimal(10,0) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_6BD70C0FF675F31B` (`author_id`),
  CONSTRAINT `Book_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `Author` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1701 DEFAULT CHARSET=utf8;
</pre>
<p>Danach habe ich folgende Testszenarien definiert und pro Benchmark entsprechend umgesetzt:</p>
<p><strong>Test-Szenario 1:</strong><br />
Erstellen eines Modell-Objekts, Setzen und Speichern von Attributen. Hier wurde<br />
die Model-Objekt-Geschwindigkeit und die INSERT-Statement-Generierung gemessen. Je 1700 Mal ausgeführt</p>
<p><strong>Test-Szenario 2:</strong><br />
Sucht einen Eintrag anhand des Primärschlüssels. Hier wurden das grundlegende Abfrage-Prinzip und die Objekt-Hydratation gemessen. Je 1700 Mal ausgeführt.</p>
<p><strong>Test-Szenario 3:</strong><br />
Sucht einen Datensatz mit einer komplexen Abfrage. Hier wurde die Objektabfrage-Geschwindigkeit gemessen. Je 1900 Mal ausgeführt.</p>
<p><strong>Test-Szenario 4:</strong><br />
Liefert fünf Datensätze für ein einfaches Kriterium, etwa Book.price=???. Hier wurde die Hydratations Geschwindigkeit gemessen. Je 190 Mal ausgeführt.</p>
<p><strong>Test-Szenario 5: </strong><br />
Liefert einen Datensatz zusammen mit dem dazugehörigen Hydrat-Datensatz aus seiner Referenz-Tabelle. Hier wurde die Geschwindigkeit beim JOIN-Abfragen gemessen. Je 700 Mal ausgeführt.</p>
<p>Jede Abfrage-Möglichkeit wurde isoliert durch die oben beschriebenen Test-Szenarien ausgeführt. Dabei habe ich versucht, einen Zustand wie im Echt-Betrieb zu simulieren.</p>
<h3>Testergebnisse und harte Fakten</h3>
<p>1 Durchlauf zum aufwärmen:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL	  |    471 |    374 |    228 |    292 |    275 |     0.25559375
   PDO  	  |    495 |    369 |    220 |    312 |    276 |  0.30880859375
   Doctrine2 |    293 |   1260 |    298 |   1478 |    883 |  6.73025390625
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>2 Durchlauf:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL     |    398 |    295 |    196 |    271 |    250 |     0.25559375
   PDO       |    454 |    336 |    205 |    285 |    269 |  0.30880859375
   Doctrine2 |    271 |   1110 |    291 |   1324 |   3390 |  6.67366796875
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>3 Durchlauf:</p>
<pre name="code" class="php">
             | insert | find   | complex| hydrate| join   |  memory MB
             |--------|--------|--------|--------|--------|----------------
   MySQL     |    411 |    304 |    202 |    275 |    244 |     0.25559375
   PDO       |    441 |    333 |    208 |    284 |    260 |  0.30880859375
   Doctrine2 |    276 |   1158 |    297 |   1341 |    839 |  6.73586328125
             |--------|--------|--------|--------|--------|----------------
</pre>
<p>Hier die jeweiligen SQL-Statements in abgekürzter Form, um sehen zu können, was die obigen Test-Probanden an SQL im Hintergrund erzeugen</p>
<p><a href="https://gist.github.com/1205769" >SQL-Statements erstellt von MySQL während des Benchmarks </a><br />
<a href="https://gist.github.com/1205765" >SQL-Statements erstellt von PDO während des Benchmarks</a><br />
<a href="https://gist.github.com/1205757" >SQL-Statements erstellt von Doctrine2 während des Benchmarks</a> </p>
<h3>Erkenntnisse</h3>
<p>Für die unten aufgeführten Erkenntnisse habe ich mich mehr auf Doctrine2 beschränkt.</p>
<h4>INSERT-Statements</h4>
<p>Alle INSERT-Statements werden immer gebündelt und gemeinsam in einer Transaktion ausgeführt. Man bekommt hierfür in etwa ein Gefühl dafür, wie die Geschwindigkeit sich bei einem Massen-Import verhält </p>
<h4>Suche anhand des Primärschlüssels</h4>
<p>Ohne es mit einen nativen PHP-MySql Abfrage-Art zu vergleichen, möchte ich behaupten, dass Doctrin2 langsamer ist. Hier haben wir jedoch ein ORM mit viel Abstraktion. Letztendlich ging das Abfragen mit DOctrine2 ganz bequem und das erwartete Reslut-Objekt wurde ausgeliefert. Doctrine2 bietet mehrere Möglichkeiten für das Ausliefern eines Abfrage-Ergebnisses: Als Modell-Objekt, als Array-Hydrat, als Scalar-Hydrat, als Modell-Objekt mit Verwendung des internen Array-Cache, als Modell-Objekt ohne Verwendung der internen Proxy-Klassen (zuständig für das Überladen) und als Iterator-Hydrat.</p>
<h4>Suche anhand einer komplexen Abfrage</h4>
<p>Hierfür wurde ein Statement mit Bedingungen WHERE+OR+CONCAT kombiniert verwendet. Die Suchergebnisse wurden ebenfalls schnell ausgeliefert. Dieselbe Abfrage mitunter Verwendung des internen Array-Cache war wie erwartet &#8211; schneller.</p>
<h4>Auslieferung von Suchergebnissen als nicht Doctrine2-Model-Objekte</h4>
<p>Wie oben im Text beschrieben, können die Suchergebnisse als Array oder Scalar-Object ausgeliefert werden. Diese können wiederum mit Verwendung vom internen Caching optional mitArray, Memory oder APC ausgeführt werden. Zudem hat man die Möglichkeit, das Ausliefern der Suchergebnisse ohne das interne Überladen (lazy-loading) durchzuführen. Diese Möglichkeit (also lazy-loading, überschreiben) sollte man jedoch auslassen, da sich die ausgelieferten Objektmodelle instabil zu ihren Entitäten verhalten können. Nichtsdestotrotz, alle drei Kombinationen haben sich als ähnlich schnell erwiesen, nur bei den JOIN-Statements war ein kleiner, jedoch unwesentlicher, Unterschied zu vermerken.</p>
<h4>Caching und Cache-Validation</h4>
<p>Doctrine2 bringt mehrere Caching Mechanismen: APC, Memcache, Xcache und ArrayCache. Es bietet die gängigen Caching Operationen. Das Finden und Löschen kann nach Angabe eines Suffixes, Präfixes, Regulären-Ausdrucks und Namespaces erfolgen. Es werden also viele Möglichkeiten geboten, nach bestimmten Kriterien eine Gruppe aus dem Cache zu identifizieren und oder sie zu löschen.</p>
<p>Hierfür habe ich einen ganz einfachen Test ausgeführt:</p>
<p>1. Gib mir das Modellobjekt aus dem Repository mit der id=1<br />
2. Speichere diese in den Cache unter der cacheId=cache_id123 für 3sek.<br />
3. Hole das Modell-Objekt aus dem Cache und vergleiche es mit dem Model-Oobjekt aus dem Repository.<br />
4. Halte den Prozess für vier Sekunden an und versuche, das Objekt aus dem Cache zu laden.<br />
<br />
Hier die Ausgabe in der Konsole:</p>
<pre>
---------- Repository before Cache ----------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Repository == Cache -------------
bool(false)
---------- Repository ---------------------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Cache --------------------------
Attribute: 1 color blue
Attribute: 1 name dog
Content: hallo wie geht es dir?
---------- Sleep 4sec, get data Cache ------
bool(false)
</pre>
<p></p>
<p>
Beide Objekte sind von derselben Instanz und haben denselben Inhalt, werden jedoch nicht als gleich identifiziert. Doctrin2 biete also das gängige Caching-Verhalten und sorgt nicht dafür, dass auch die Objekte im Cache konsistent zur Datenbank bleiben.</p>
<p>Weitere und detaillierte Information über den Cache gibt es hier: http://www.doctrine-project.org/docs/orm/2.0/en/reference/caching.html
</p>
<h4>Massen Inserts &#038; Massen Object Processing</h4>
<p>Doctrine2 unterstützt keine multi-INSERTs wie „(insert into (…) values (…), (…), (…),..“. Dafür werden die INSERT-Statements jeweils einzeln ausgeführt. Laut Doctrine2 soll das perfomanter sein. Zudem sind die Single-INSERT-Statements für Doctrine2 leichter in anderen DB-Systemen zu übertragen, als multi-INSERT-Statements. Für eine initiale Massen-Migration ist das Doctrine2 eher nicht gedacht. Jedoch bietet es eine Art von “Batch Processing”. Damit ist es möglich, die Massen-Migration von Daten in fest definierten Chargen abzuarbeiten bzw. in die Datenbank zu migrieren. Die Ausführungszeiten und der Speicherverbrauch sind mehr als akzeptabel. Wenn man oft mit sehr vielen Daten arbeiten muss, dann gibt es die Möglichkeit, sich die Daten als “Iterable-Result” ausliefern zu lassen und darüber zu iterieren. In diesem Fall ist das schneller, als wenn man sich die Daten als Hydrat-Objekt ausliefern lässt. Wem das aber immer noch nicht schnell genug ist, dem rät Doctrine2, interne Datenbank-Optionen für Massen-Importe zu verwenden. Wie bei MySQL “LOAD DATA INFILE”.</p>
<p>Mehr Information und einen ausführlichen Bericht hier:<br />
-http://www.doctrine-project.org/blog/doctrine2-batch-processing<br />
-http://www.doctrine-project.org/docs/orm/2.0/en/reference/batch-processing.html</p>
<h4>Arbeiten mit nicht relationalen Datenbanken</h4>
<p>Der Vorteil von NoSQL Datenbanken gegenüber MySQL oder jeder anderen RDBMS ist ganz klar die Performance. Für Doctrine2 gibt es PlungIns auf GitHub zum Herunterladen. Diese können laut Dokumentation auch alle gängigen Entitäten abbilden. Der Unterschied liegt in der Deklaration der Annotationen in den Modell-Klassen. Für ein Umsteigen von SQL auf NoSQL Datenbank ist also eine umfassende Anpassung an der Projekt-Konfiguration und der Modell-Klasse notwendig. Abfrage-Methoden wie find*(…) oder getRepository(…) müssen nicht angepasst werden.</p>
<p>Mehr Information:<br />
- http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/index.html<br />
- http://www.doctrine-project.org/blog/mongodb-for-ecommerce<br />
- http://www.doctrine-project.org/docs/couchdb_odm/1.0/en/index.html<br />
- mongodb-odm https://github.com/doctrine/mongodb-odm<br />
- couchdb-odm https://github.com/doctrine/couchdb-odm</p>
<h4>Erstellen von Custom Mapping Types</h4>
<p>In Doctrine2 ist es möglich, auch eigene Spalten-Typen zu definieren. Ich habe es mit dem Typ “email” getestet. Dafür muss im Projekt ein neues Verzeichnis “Types” erstellt werden. In der Konfigurationsdatei muss der neue Typ bekannt gemacht werden:</p>
<pre name="code" class="php">
//...
use Doctrine\DBAL\Types\Type;
//...
Type::addType('email', 'Types\Email');
//...
$conn = $em->getConnection();
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('email', 'email');
</pre>
<p>
Und so sieht die Klasse des neuen email-Types aus. Eine einfache E-Mail-Validierung ist auch eingebaut.<br />
</p>
<pre name="code" class="php">
namespace Types;
use Doctrine\DBAL\Types\ConversionException,
    Doctrine\DBAL\Types\Type,
    Doctrine\DBAL\Platforms\AbstractPlatform;
class Email extends Type
{
    const MYTYPE = 'email';
    /** @override */
    public function convertToDatabaseValue($value, \Doctrine\DBAL\Platforms\AbstractPlatform $platform)
    {
      if (false === filter_var($value, FILTER_VALIDATE_EMAIL))
      {
        throw ConversionException::conversionFailed($value, $this->getName());
      }
      return $value;
    }
    /** @override */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
      return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration);
    }
    /** @override */
    public function getDefaultLength(AbstractPlatform $platform)
    {
      return $platform->getVarcharDefaultLength();
    }
    public function getName()
    {
        return self::MYTYPE;
    }
}
</pre>
<p>Nach dem Setup, wurde die Tabelle Author wie erartet ohne Probleme erweitert und gültige E-Mails konnten abgespeichert werden. </p>
<h3>Alle Features in einer Tabelle</h3>
<table id="one-column-emphasis" summary="MySQL-Functions vs. PDO vs. Doctrine2">
<colgroup>
<col class="oce-first" />
    </colgroup>
<tbody>
<tr>
<td> <b>ORM</b> </td>
<td> <b>Prepared Statements</b> </td>
<td> <b>Transactions</b> </td>
<td> <b>Caching</b> </td>
<td> <b>Exceptions</b> </td>
<td> <b>Mass insert</b> </td>
<td> <b>Object Hydration</b> </td>
<td> <b>Scaffolding</b> </td>
<td> <b>Reverse Engineering</b> </td>
<td> <b>Drivers</b> </td>
</tr>
<tr>
<td style="text-align: right" class="align-right"> MySQL &amp; Active Record </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> + </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> </td>
<td> ++ </td>
<td style="text-align: right" class="align-right"> + </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td> MySQL </td>
</tr>
<tr>
<td style="text-align: right" class="align-right"> PDO &amp; Active Record </td>
<td> ++ </td>
<td> + </td>
<td> +- </td>
<td> ++ </td>
<td> + </td>
<td style="text-align: right" class="align-right"> ++ </td>
<td style="text-align: right" class="align-right"> +- </td>
<td style="text-align: right" class="align-right"> &nbsp; </td>
<td style="text-align: left" class="align-left"> MySQL, Cubrid, FreeTDS, Firebird, DB2, Informix, Oracle, ODBC, PostgreSQL, SQLite, 4D, MS SQL </td>
</tr>
<tr>
<td style="text-align: left" class="align-left"> Doctrine2 </td>
<td> +++ </td>
<td> ++ </td>
<td> +++ </td>
<td> +++ </td>
<td> +++ </td>
<td style="text-align: right" class="align-right"> +++ </td>
<td style="text-align: right" class="align-right"> ++ </td>
<td style="text-align: right" class="align-right"> +++ </td>
<td> MySQL, Cubrid, FreeTDS, Firebird, DB2, Informix, Oracle, ODBC, PostgreSQL,SQLite, 4D, MS SQL, MongoDB, CouchDB </td>
</tr>
</tbody>
</table>
<p><strong>Prepared Statements:</strong> bei der MySQL Lösung muss man leider selber Methoden schreiben, um Prepared Statements ausführen zu können. PDO und DOctrine2 bringen das von Haus aus mit.</p>
<p><strong>Transactions:</strong> alle Test-Probanden unterstützen das. Wobei bei MySQL die Methoden erst geschrieben werden müssen. Lustig wird das bei Nested-Transactions. Doctrine2 kann das, für PDO und MySQL muss das implementiert werden.</p>
<p><strong>Caching:</strong> bei dem MySQL- und PDO-Probanden muss das Caching implementiert werden, Doctrine2 hingegen bringt das mit von Haus aus mit.</p>
<p><strong>Mass insert:</strong> das unterstützen alle drei Probanden. </p>
<p><strong>Exceptions:</strong> bei MySQL muss man die Exceptions implementieren. PDO und Doctrine2 bringen diese von Haus aus mit.</p>
<p><strong>Object Hydration:</strong> alle drei Probanden können ein Result-Set als Object zurückliefen. Doctrine2 kann jedoch noch mehr: als Array-Object, Iretable-Object und Model-Object.</p>
<p><strong>Scaffolding:</strong> bei den MySQL- und PDO-Probanden muss das erst implementiert werden, Doctrine2 bringt das von Haus aus mit.</p>
<p><strong>Reverse Engineering:</strong> nur der Doctrin2-Proband kann das. Ein paar mögliche Operationen wären: SQL-Datei in die Datenbank importieren, internes Caching Operationen, konvertieren von diversen Datenbank-Shema ins PHP, Model- und Mapper-Klassen aus der Datenbank generieren. </p>
<p><strong>Drivers:</strong> bei MySQL ist es klar, nur MySQL Datenbank. PDO kann viele SQL basierende Datenbanksysteme unterstützen. Bei Doctrine2 ist es genauso wie bei PDO, da es auf PDO basisiert. Für Doctrine2 gibt es zusätzlich noch Driver als Add-ons, die MongoDB oder CouchDB unterstützen.</p>
<h3>Gedanke</h3>
<p>Wenn ein Team zum ersten mal mit Modellen/Objekten arbeiten möchte, dann sollte es sich zuerst mit dem Active-Record-Pattern gut vertraut machen. Ist auch leicht zum umsetzen, kontrollieren, erweitern, optimieren und debuggen. Ein typisches ORM hingegen basiert auf das Repository-Pattern. Dieses macht viel Magie unter der Haube. Ist daher nicht einfach zu kontrollieren und debuggen. Man macht sich von ein ORM abhängig. Mann muss das ORM auch nicht komplett verwenden. Es reicht oft auch, wenn man nur das manipulieren der Datenbank dem ORM überlässt. Bei diesem Punkt möchte ich es nicht verallgemeinern, den der genaue Einsatz eines ORM hängt von der Anforderung des Projektes und Teams ab.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/mysql-functions-vs-pdo-vs-doctrine2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Search with Solr</title>
		<link>http://www.phphatesme.com/blog/mysql/search-with-solr/</link>
		<comments>http://www.phphatesme.com/blog/mysql/search-with-solr/#comments</comments>
		<pubDate>Tue, 07 Jun 2011 13:00:00 +0000</pubDate>
		<dc:creator>Nils Langner</dc:creator>
				<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[Vorträge]]></category>

		<guid isPermaLink="false">http://www.phphatesme.com/blog/mysql/search-with-solr/</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<p>With Google constantly pushing the customer expectations of searching, is it time to move away from our database full-text search in pursuit of a more targeted platform? Can implementing Solr offer more than an answer to a search? Implementing a search platform isn’t always suitable for all applications, but in this talk we’ll look at identifying the right search solution, choosing the best way to integrate it into our application and exploring all the benefits a search server can offer.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/search-with-solr/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL: Doppelte Datensätze löschen</title>
		<link>http://www.phphatesme.com/blog/mysql/mysql-doppelte-datensatze-loschen/</link>
		<comments>http://www.phphatesme.com/blog/mysql/mysql-doppelte-datensatze-loschen/#comments</comments>
		<pubDate>Wed, 12 Jan 2011 05:00:20 +0000</pubDate>
		<dc:creator>ebene7</dc:creator>
				<category><![CDATA[Datenbanken]]></category>
		<category><![CDATA[phmnetwork]]></category>

		<guid isPermaLink="false">http://blog.ebene7.com/?p=2552</guid>
		<description><![CDATA[Hin und wieder kann es vorkommen, dass sich Datensätze ungewollt doppelt in der Datenbank wiederfinden.
Hier ein kleiner Spickzettel, wie sich die Doubletten wieder entfernen lassen.
Die Struktur der Testtabelle sieht folgendermaßen aus:
CREATE TABLE `sqltest`.`duplicates` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `a` VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `b` INT(11) NOT [...]]]></description>
			<content:encoded><![CDATA[<p>Hin und wieder kann es vorkommen, dass sich Datensätze ungewollt doppelt in der Datenbank wiederfinden.</p>
<p>Hier ein kleiner Spickzettel, wie sich die Doubletten wieder entfernen lassen.</p>
<p><span id="more-2552"></span>Die Struktur der Testtabelle sieht folgendermaßen aus:</p>
<pre><code>CREATE TABLE `sqltest`.`duplicates` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `a` VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `b` INT(11) NOT NULL ,
  `c` DATETIME NOT NULL ,
PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;</code></pre>
<p>Noch ein paar Daten rein und fertig ist die Spielwiese.</p>
<pre>mysql&gt; select * from duplicates;
+----+--------+-----+---------------------+
| id | a      | b   | c                   |
+----+--------+-----+---------------------+
|  1 | Apfel  | 123 | 2011-01-11 17:00:00 |
|  2 | Apfel  | 123 | 2011-01-11 17:00:00 |
|  3 | Banane | 234 | 2011-01-12 17:00:00 |
|  4 | Banane | 234 | 2011-01-12 17:00:00 |
|  5 | Birne  | 456 | 2011-01-15 17:00:00 |
|  6 | Birne  | 456 | 2011-01-15 17:00:00 |
+----+--------+-----+---------------------+
6 rows in set (0.00 sec)</pre>
<p>Die erste Abfrage macht einen JOIN auf sich selbst, dafür die Aliasse d1 und d2 und dient zunächst der Prüfung, ob wir die gewünschten Datensätze geliefert bekommen.</p>
<pre>SELECT d1.* FROM duplicates d1, duplicates d2
 WHERE d1.id != d2.id
   AND d1.a = d2.a
   AND d1.b = d2.b
   AND d1.c = d2.c
   AND d1.id &lt; d2.id</pre>
<p>Das Ergebnis:</p>
<pre>+----+--------+-----+---------------------+
| id | a      | b   | c                   |
+----+--------+-----+---------------------+
|  1 | Apfel  | 123 | 2011-01-11 17:00:00 |
|  3 | Banane | 234 | 2011-01-12 17:00:00 |
|  5 | Birne  | 456 | 2011-01-15 17:00:00 |
+----+--------+-----+---------------------+
3 rows in set (0.00 sec)</pre>
<p>MySQL kann offensichtlich keine Daten löschen, wenn die Löschaktion auf derselben Tabelle wie die Subquery gemacht wird. Darum schreiben wir die Ids in eine temporäre Tabelle.</p>
<pre>CREATE TABLE duplicates_ids AS (
  SELECT d2.id FROM duplicates d1, duplicates d2
   WHERE d1.id != d2.id
     AND d1.a = d2.a
     AND d1.b = d2.b
     AND d1.c = d2.c
     AND d1.id &lt; d2.id
)</pre>
<p>Im letzten Schritt löschen wir die zuvor ausgewählten Datensätze und auch die temporäre Tabelle. Je nachdem was wir behalten wollen, noch ggf. ein &#8220;NOT&#8221; vor das &#8220;IN&#8221; schreiben.</p>
<pre>DELETE FROM duplicates WHERE id [NOT] IN (SELECT * FROM duplicates_ids);
DROP TABLE duplicates_ids;</pre>
<p>Achtung! Ich hafte nicht bei Datenverlust oder sonstigen Fehlern. Ein Backup sollte sicherheitshalber vorher gemacht werden.</p>


<!-- Begin SexyBookmarks Menu Code -->
<div class="sexy-bookmarks sexy-bookmarks-expand sexy-bookmarks-center sexy-bookmarks-bg-caring-old">
<ul class="socials">
		<li class="sexy-comfeed">
			<a href="http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/feed" rel="nofollow" class="external" title="Abonniere die Kommentare für diesen Beitrag">Abonniere die Kommentare für diesen Beitrag</a>
		</li>
		<li class="sexy-twitter">
			<a href="http://twitter.com/home?status=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen+-+http://bit.ly/eWVmib+&amp;source=shareaholic" rel="nofollow" class="external" title="Twittere diesen Artikel!">Twittere diesen Artikel!</a>
		</li>
		<li class="sexy-facebook">
			<a href="http://www.facebook.com/share.php?v=4&amp;src=bm&amp;u=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;t=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen" rel="nofollow" class="external" title="Empfehle diesen Artikel bei Facebook">Empfehle diesen Artikel bei Facebook</a>
		</li>
		<li class="sexy-misterwong">
			<a href="http://www.mister-wong.de/addurl/?bm_url=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;bm_description=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen&amp;plugin=sexybookmarks" rel="nofollow" class="external" title="Füge hinzu zu Mister Wong">Füge hinzu zu Mister Wong</a>
		</li>
		<li class="sexy-delicious">
			<a href="http://delicious.com/post?url=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;title=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen" rel="nofollow" class="external" title="Empfehle diesen Artikel bei del.icio.us">Empfehle diesen Artikel bei del.icio.us</a>
		</li>
		<li class="sexy-technorati">
			<a href="http://technorati.com/faves?add=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/" rel="nofollow" class="external" title="Empfehle diesen Artikel bei Technorati">Empfehle diesen Artikel bei Technorati</a>
		</li>
		<li class="sexy-stumbleupon">
			<a href="http://www.stumbleupon.com/submit?url=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;title=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen" rel="nofollow" class="external" title="Bei StumbleUpon einstellen">Bei StumbleUpon einstellen</a>
		</li>
		<li class="sexy-digg">
			<a href="http://digg.com/submit?phase=2&amp;url=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;title=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen" rel="nofollow" class="external" title="Digg this!">Digg this!</a>
		</li>
		<li class="sexy-myspace">
			<a href="http://www.myspace.com/Modules/PostTo/Pages/?u=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;t=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen" rel="nofollow" class="external" title="Sende dies zu MySpace">Sende dies zu MySpace</a>
		</li>
		<li class="sexy-linkedin">
			<a href="http://www.linkedin.com/shareArticle?mini=true&amp;url=http://blog.ebene7.com/2011/01/12/mysql-doppelte-datensaetze-loeschen/&amp;title=MySQL%3A+Doppelte+Datens%C3%A4tze+l%C3%B6schen&amp;summary=Hin%20und%20wieder%20kann%20es%20vorkommen%2C%20dass%20sich%20Datens%C3%A4tze%20ungewollt%20doppelt%20in%20der%20Datenbank%20wiederfinden.%0D%0A%0D%0AHier%20ein%20kleiner%20Spickzettel%2C%20wie%20sich%20die%20Doubletten%20wieder%20entfernen%20lassen.%0D%0A%0D%0ADie%20Struktur%20der%20Testtabelle%20sieht%20folgenderma%C3%9Fen%20aus%3A%0D%0ACREATE%20TABLE%20%60sqltest%60.%60duplicates%60%20%28%0D%0A%20%20%60id%60%20INT%2811%29&amp;source=ebene7" rel="nofollow" class="external" title="Empfehle diesen Artikel bei LinkedIn">Empfehle diesen Artikel bei LinkedIn</a>
		</li>
</ul>
<div style="clear:both;"></div>
</div>
<!-- End SexyBookmarks Menu Code -->

]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/mysql-doppelte-datensatze-loschen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NoSQL ist das neue AJAX, oder: Warum brauche ich NoSQL ?</title>
		<link>http://www.phphatesme.com/blog/mysql/nosql-ist-das-neue-ajax-oder-warum-brauche-ich-nosql/</link>
		<comments>http://www.phphatesme.com/blog/mysql/nosql-ist-das-neue-ajax-oder-warum-brauche-ich-nosql/#comments</comments>
		<pubDate>Mon, 20 Dec 2010 06:01:15 +0000</pubDate>
		<dc:creator>Oliver Kurowski</dc:creator>
				<category><![CDATA[Datenbanken]]></category>

		<guid isPermaLink="false">http://www.phphatesme.com/?p=7573</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<p>Herzlich Willkommen zu meinen Beiträgen, die hauptsächlich von NoSQL und dem Drumherum handeln werden.<br />
Meine Sicht auf NoSQL ist die eines Datenbankentwicklers der nicht in einer Agentur, sondern in der Firma sitzt, die die Daten produziert und konsumiert. Hierbei handelt es sich um Shopsysteme mit nicht-trivialen Produkten, deren Eigenschaften auch noch je nach Abhängigkeit zu anderen Faktoren veränderbar sind. Kurzum: Wir verkaufen Autoteile.</p>
<p>Warum NoSQL?</p>
<p>NoSQL ist zu einem Oberbegriff von Datenbanksystemen geworden, die weit mehr Eigenschaften haben als nur der Verzicht auf SQL als Abfragesprache. Ich denke, die Eckdaten der am meisten verbreiteten Systeme sind klar:</p>
<ul>
<li>Hohe horizontale Skalierbarkeit (horizontal=viele Rechner, vertikal=ein großes, schnelles System)</li>
<li>Eingeschränkte Verknüpfungen von Datensätzen untereinander (JOIN)</li>
<li>Keine Tabellen, in die die Daten gezwängt werden</li>
<li>Kein SQL als Abfragesprache</li>
</ul>
<p>Warum aber NoSQL?</p>
<p>Warum soll man sich neuer Technik bedienen, wenn die Alte (Technik!) doch bislang immer funktioniert hat ?<br />
Der Grund &#8220;weil&#8217;s Spaß macht&#8221; ist aus Entwicklersicht einsichtig, aus betriebswirtschaftlicher Sicht eher weniger.<br />
Wer sich die gängigen NoSQL-Beispiele ansieht, kommt recht schnell auf die Idee, daß anscheinend außer Blogs nicht viel mehr mit NoSQL gemacht werden kann. Macht es Sinn, eine relational funktionierende Produktdatenbank mit den NoSQL-Features umzusetzen?</p>
<p>Aus Jux und dollerei wird kaum jemand ein bestehendes Projekt auf die neue Technologie umändern. Ist auch nicht immer nötig, aber wie wäre es damit, Teile zu ersetzen?</p>
<p>Eine Produktdatenbank hat immer zwei Zugriffszenarien: Die Daten werden erstellt, oder gelesen. Anders als bei Twitter und Co sind die Zugriffszahlen beim Lesen immens höher als beim Schreiben. Und was macht SQL? Um dem Kunden Die passenden Produkte anzuzeugen, geht dem meist ein Wust an JOINS voraus. Tabellen mit Fahrzeuginformationen, Produktstamm, Eigenschaften in Abhängigkeit vom Fahrzeugen, Preistabellen, Sperrungen und Abhängigkeiten von anderen Produkten&#8230; all das wird einmal erstellt, aber tausendfach abgerufen. Zur Optimierung der Zugriffszeiten beim Lesen müssen Cachingmechanismen herhalten.</p>
<p>Speicher ist billig, Zeit ist teuer.. und da kommen die Schemalosen Datenbanken ins Spiel. So kann, nachdem ein Produkt angelegt und mit seinen Eigenschaften versehen ist, dieses Produkt mit all seinen Eigenschaften und Abhängigkeiten einfach als Dokument in eine NoSQL Datenbank gespeichert werden. Dass nun jedes Produkt auch alle passenden Fahrzeuge beinhaltet (und damit die Fahrzeuge hundertfach in den Produkten vorkommen) ist aus Normierungssicht horrend redundant. Aber aus zeitlicher Sicht ist eine 10000-fache JOIN-Orgie gegenüber einer einmaligen Zusammenstellung aller Daten zu einem Produktdokument ebenso redundant.</p>
<p>Es ist also ein kleiner erster Schritt, die vorhandenen relationalen Daten in einem Exportvorgang zusammenzufassen und als Dokumente in eine Schmalose Datenbank zu packen.</p>
<p>Die Wahl fiel bei uns auf die CouchDB, da sie den Gedanken der Geschwindigkeitsoptimierung beim lesen vor dem des Schreibens durch vordefinierte Abfragen konsequent umsetzt.</p>
<p>Ich werde Ihnen in regelmäßigen Abständen die Ideen und Hürden hinter dieser Umsetzung nahe bringen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phphatesme.com/blog/mysql/nosql-ist-das-neue-ajax-oder-warum-brauche-ich-nosql/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
	</channel>
</rss>

