<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marko Pađen</title>
    <description>The latest articles on DEV Community by Marko Pađen (@markopaden).</description>
    <link>https://dev.to/markopaden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F345748%2F02e2c791-3b6b-4ccd-b5ae-da2ec84eda30.png</url>
      <title>DEV Community: Marko Pađen</title>
      <link>https://dev.to/markopaden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/markopaden"/>
    <language>en</language>
    <item>
      <title>Doing OneToMany properly with Doctrine</title>
      <dc:creator>Marko Pađen</dc:creator>
      <pubDate>Thu, 30 Jul 2020 09:07:24 +0000</pubDate>
      <link>https://dev.to/bornfightcompany/doing-onetomany-properly-with-doctrine-3flm</link>
      <guid>https://dev.to/bornfightcompany/doing-onetomany-properly-with-doctrine-3flm</guid>
      <description>&lt;p&gt;Using ORM frameworks like &lt;a href="https://www.doctrine-project.org/"&gt;Doctrine&lt;/a&gt; can help us a great deal when writing small to medium sized web applications. It allows us to treat database rows like objects, therefore simplifying working with them in object oriented programming languages. With ORM, we don't have to worry about things like fetching and saving since all of that happens "magically" behind the framework. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;As always, simplifications and abstraction come at a cost - with ORMs it's usually performance. Without going too deep into it(there are several great &lt;a href="http://ocramius.github.io/blog/doctrine-orm-optimization-hydration/"&gt;articles&lt;/a&gt; that explain how ORM magic works), we can say that ORM needs to ensure that objects you get are a mirror image of the state of the database. &lt;/p&gt;

&lt;p&gt;Let's say we have User entity that can have multiple Addresses. Fetching Users should also fetch their addresses, since we should be able to call &lt;code&gt;$user-&amp;gt;getAddresses()&lt;/code&gt;. It should never happen that this call returns no Addresses, if the user in fact has some Addresses stored. Therefore, ORM needs to do a bit more work than it is usually needed. &lt;/p&gt;

&lt;p&gt;Modern ORMs usually do some sort of &lt;em&gt;lazy loading&lt;/em&gt; to overcome that, but it is not always possible. Because of ORMs magic, you can make life really hard for yourself if you don't understand how it works. &lt;strong&gt;Properly setup relations are a key to any maintainable application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ORM doesn't like bidirectional relationships. In the example above, the relation is bidirectional if you are able to call both&lt;br&gt;
&lt;code&gt;$user-&amp;gt;getAddresses()&lt;/code&gt; and &lt;code&gt;$address-&amp;gt;getUser()&lt;/code&gt;. To make this possible, ORM needs to do what's called hydration. As our database grows, this process can become really expensive and slow down our application.&lt;/p&gt;

&lt;p&gt;In cases like this, we should ask ourselves: Do we really need both directions? It is very unlikely that you will have Address and need to fetch its User. If you do need that, you might have structural problems in your application. Address is meaningless without User, so we should never work with it without first having User.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We should avoid bidirectional relationships whenever possible.&lt;/strong&gt; With Doctrine, it's a bit trickier to do unidirectional OneToMany relation. &lt;/p&gt;
&lt;h2&gt;
  
  
  How not to.
&lt;/h2&gt;

&lt;p&gt;Working with the example above, we have Address and User entities.&lt;/p&gt;

&lt;p&gt;If we do OneToMany the normal way, it is always bidirectional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//fields&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var Collection[]
     * @ORM\OneToMany(targetEntity=Address::class, mappedBy="user", orphanRemoval=true,  cascade={"persist", "remove"})
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$addresses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our Address entity now needs to have &lt;code&gt;$user&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//fields&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var User|null
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="addresses")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Looking at the database, we can see that our Address table has user_id column. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;country_id&lt;/th&gt;
&lt;th&gt;street&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;th&gt;postal_code&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This setup might look perfectly fine, but there are a couple of big problems. We can see that in this case, the owner of the relation is actually Address! The only direction we can remove is the inverse one &lt;code&gt;$user-&amp;gt;getAddresses()&lt;/code&gt;. This forces us to use a bidirectional relation that might cause performance problems on hydration level as our database grows.&lt;/p&gt;

&lt;p&gt;What if we want to reuse our Address model? Let's say we have a Company entity that can also have multiple Addresses. We can add OneToMany relation to Company as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nx"&gt;Address&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//fields&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var User|null
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="addresses")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var Company|null
     * @ORM\ManyToOne(targetEntity=Company::class, inversedBy="addresses")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$company&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Suddenly, our address not only knows about User, but about Company as well! On top of that, without validation Address &lt;br&gt;
entity can have both User and Company relations at the same time. We probably didn't want that.&lt;/p&gt;

&lt;p&gt;Our address table in the database now looks like this.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;country_id&lt;/th&gt;
&lt;th&gt;street&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;th&gt;postal_code&lt;/th&gt;
&lt;th&gt;user_id&lt;/th&gt;
&lt;th&gt;company_id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It should be obvious at this point that this is not the way to go. Address is a value object and should not have references to other entities.&lt;/p&gt;
&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;To do unidirectional OneToMany properly, we should in fact use ManyToMany relation with unique constraint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;//fields&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var Collection[]
     * @ORM\ManyToMany(targetEntity=Address::class, cascade={"persist", "remove"}, orphanRemoval=true)
     * @ORM\JoinTable(name="user_addresses",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="cascade")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="address_id", referencedColumnName="id", unique=true)}
     *      )
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$addresses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, our Address entity does not have to know about its owners. That information is stored in the join table and ORM doesn't have to perform unnecessary hydration. Also, adding another owner of Address entity does not impact the Address table or entity.&lt;/p&gt;

&lt;p&gt;We shouldn't be afraid of the join table as it is hidden from us by the ORM. Performance wise, joining tables by integer foreign keys is a relatively &lt;a href="https://www.brianlikespostgres.com/cost-of-a-join.html"&gt;cheap&lt;/a&gt; operation for our database.&lt;/p&gt;

&lt;p&gt;Our Address entity now looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;


&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var int|null
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var string
     * @ORM\Column(type="string", length=255)
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$street&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var string
     * @ORM\Column(type="string", length=255)
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$city&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var string
     * @ORM\Column(type="string", length=255)
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$postalCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var Country
     * @ORM\ManyToOne(targetEntity=Country::class)
     * @ORM\JoinColumn(nullable=false)
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$country&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And our address table doesn't have any foreign keys.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;country_id&lt;/th&gt;
&lt;th&gt;street&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;th&gt;postal_code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can use our entities the same way we did before, just without unnecessary bidirectional relation. Later on if we ever need the other direction, we can add it to our code without changing our database structure.&lt;/p&gt;

&lt;p&gt;Do you smell the cleanliness of this solution? :)&lt;br&gt;
Are you using unidirectional relations in your applications?&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>database</category>
      <category>doctrine</category>
    </item>
    <item>
      <title>Custom identifiers with ApiPlatform</title>
      <dc:creator>Marko Pađen</dc:creator>
      <pubDate>Mon, 20 Jul 2020 12:00:56 +0000</pubDate>
      <link>https://dev.to/bornfightcompany/custom-identifiers-with-apiplatform-529j</link>
      <guid>https://dev.to/bornfightcompany/custom-identifiers-with-apiplatform-529j</guid>
      <description>&lt;p&gt;When developing Web APIs, you often need to think about your resource identifiers. Resource identifier can be anything from simple incrementing integer to more complex uuid fields. The only real constraint is that the field is unique.&lt;/p&gt;

&lt;p&gt;By default, ApiPlatform is going to use the field named &lt;code&gt;id&lt;/code&gt; for your identifier. If you are using Doctrine as your ORM, that field will usually be a simple integer field with auto-increment option. &lt;/p&gt;

&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;There are a number of reasons why you would want to use something else as a resource identifier for your API. If your API is publicly available, exposing the internal database id might also expose the information you don't want everyone to know. For example, knowing that your purchase has &lt;code&gt;id = 17&lt;/code&gt; might tell you that this was only the 17th purchase on that webpage. Also, you might be able to iterate over that id and see the order that resources were created. Or even worse, find some resources you are not supposed to find (although, that usually indicates you have a different sort of security problem).&lt;/p&gt;

&lt;p&gt;Another information you are exposing is &lt;em&gt;data velocity&lt;/em&gt;. For example, by comparing the IDs of different Facebook posts you might see how many new posts Facebook has every day. To summarise, these are the main problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data count&lt;/li&gt;
&lt;li&gt;Data iteration&lt;/li&gt;
&lt;li&gt;Data velocity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that we can solve the first problem by offsetting the starting id number (by setting it to something like &lt;code&gt;13000&lt;/code&gt;), but the other problems are not that easy to solve.&lt;/p&gt;

&lt;p&gt;The two commonly used API ids are uuid and slug. With uuid we get a random generated string that doesn't really expose any information about resource. Another advantage we get with uuid is that it can be client generated. This can help when creating relations before the resources are even stored in the database.&lt;/p&gt;

&lt;p&gt;Slug can also be used as a resource id, we just have to ensure that it is unique. The primary advantage of slug is that it "looks good" in the url, so it can be used to generate pretty links. If you want to get pretty urls in your frontend application without using slug as your resource id, you will have to resort to filtering.&lt;/p&gt;

&lt;p&gt;If you are using relational database, you will probably want to keep your primary key as an incrementing integer. There are performance benefits of having easily iterable field as your primary id, especially if you have a well normalized database structure.&lt;/p&gt;

&lt;h1&gt;
  
  
  How?
&lt;/h1&gt;

&lt;p&gt;To use a different identifier on an Api Platform resource, you need simply need to tell Api Platform which field to use for id. You can use &lt;a href="https://github.com/ramsey/uuid"&gt;ramsey/uuid&lt;/a&gt; for uuid generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Ramsey\Uuid\Uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * @ORM\Entity
 * @ApiResource
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Uuid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @var int|null
     * @ApiProperty(identifier=false)
     *
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var string
     * @ApiProperty(identifier=true)
     * @ORM\Column(type="uuid")
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you want to use slug, you can use &lt;a href="https://github.com/Atlantic18/DoctrineExtensions"&gt;Gedmo Sluggable&lt;/a&gt; Doctrine extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Gedmo\Mapping\Annotation&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Gedmo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * @ORM\Entity
 * @ApiResource
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var int|null
     * @ApiProperty(identifier=false)
     *
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @var string
     * @Gedmo\Slug(fields={"name"})
     * @ApiProperty(identifier=true)
     * @ORM\Column(type="string", length=128, unique=true)
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$slug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also, you need to add this configuration to &lt;code&gt;services.yaml&lt;/code&gt; to enable the slug generation before inserting it to database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="s"&gt;gedmo.listener.sluggable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gedmo\Sluggable\SluggableListener&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;doctrine.event_subscriber&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;calls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;setAnnotationReader&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@annotation_reader"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's all there is to it. Api Platform magically does the rest. We can still keep using internal id for internal stuff, and serve our api consumers with external id.&lt;/p&gt;

&lt;p&gt;What do you use as identifiers in your applications? And why?&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>apiplatform</category>
      <category>engineeringmonday</category>
    </item>
  </channel>
</rss>
