title: Symfony Doctrine Readonly Entity with data managed using migrations
body: <p>I am looking for advice on what the best practices for scenario below might be.</p>
  
  <h1>Scenario</h1>
  
  <p>I have some entities that use what I call "data as part of the schema": their data is part of the software and are not to be created/edited/deleted by the software during normal operation.</p>
  
  <p>I'm using these to provide fixed lists of choices for other entities without using enum fields - because those are really part of the column definition and if I need to alter the column in any other way than to append a new value it results in a full table copy.</p>
  
  <p>Such an entity is defined like this - readOnly=true parameter, manually assigned ID, unique slug, unique user-visible name, and private constructor. This isn't a good candidate for fixtures because it's not test data (talking about how Doctrine Fixtures wants to delete my "data as schema" is for another SO question)</p>
  
  <pre><code>/**
   * @ORM\Entity(readOnly=true)
   */
  class SubmissionStatus
  {
      /**
       * @var int
       * @ORM\Column(type="integer")
       * @ORM\Id()
       */
      protected $id;
  
      /**
       * @var string
       * @ORM\Column(type="string", length=191, unique=true)
       */
      protected $slug;
  
      /**
       * @var string
       * @ORM\Column(type="string", length=191, unique=true)
       */
      protected $name;
  
      private __construct() {}
  
      // ... other fields
  }
  </code></pre>
  
  <p>I manage the values using Doctrine Migrations:</p>
  
  <pre><code>final class Version20181023132831 extends AbstractMigration
  {
      public function up(Schema $schema) : void
      {
          $statuses = [
              ["id" =&gt; 1, "name" =&gt; "Pending", "slug" =&gt; "pending"],
              ["id" =&gt; 2, "name" =&gt; "Reported", "slug" =&gt; "reported"],
              ["id" =&gt; 3, "name" =&gt; "Rejected", "slug" =&gt; "rejected"],
              ["id" =&gt; 4, "name" =&gt; "Accepted", "slug" =&gt; "accepted"],
          ];
  
          foreach ($statuses as $status) {
              $this-&gt;addSql("INSERT INTO submission_status (id, name, slug) VALUES (:id, :name, :slug WHERE id = :id", [
                  "id" =&gt; $status['id'],
                  "name" =&gt; $status['name'],
                  "slug" =&gt; $status['slug'],
              ]);
          }
      }
  }
  </code></pre>
  
  <p>I do <code>UPDATE</code> and <code>DELETE</code> in future migrations if I need to update these values - also massaging existing Foreign Keys referring to these rows if necessary.</p>
  
  <p>Other entities have <code>@ORM\ManyToOne</code> relationships to this entity.</p>
  
  <h1>The problem</h1>
  
  <p>What is the best way to refer to these in code?</p>
  
  <p>Not all of these entities will be selected by users in <code>ChoiceType</code> fields in forms or come in through API calls. Some of them will be used just in code like <code>$otherEntity-&gt;setStatus(...)</code></p>
  
  <h1>Current solution</h1>
  
  <p>Currently we've settled on doing this:</p>
  
  <p>We're storing constants in the entity class for the IDs and slugs.</p>
  
  <pre><code>class SubmissionStatus
  {
      const PENDING_ID = 1;
      const PENDING_SLUG = 'pending';
      const REPORTED_ID = 2;
      const REPORTED_SLUG = 'reported';
      const REJECTED_ID = 3;
      const REJECTED_SLUG = 'rejected';
      const ACCEPTED_ID = 4;
      const ACCEPTED_SLUG = 'accepted';
  }
  </code></pre>
  
  <p>When we need to <code>setStatus</code>, we make database lookups:</p>
  
  <pre><code>$status = $em-&gt;getRepository(SubmissionStatus::class)-&gt;findOneBy(['slug' =&gt; SubmissionStatus::ACCEPTED_SLUG])
  $otherEntity-&gt;setStatus($status);
  </code></pre>
  
  <h1>Question</h1>
  
  <p>Is there are better way to do this?</p>
  
  <p>I'm not a big fan of writing all that <code>getRepository-&gt;findOneBy</code> boilerplate all the time, especially if I need to reference several of this kind of entities. I would like something short, easy and quick to write when doing it repeatedly.</p>
  
  <p>I would also prefer not to have to change too many places when I make changes to the values. One in the migration and one in the class constants is quite enough.</p>
  
  <h1>What I've thought of so far</h1>
  
  <p>I can't add a helper function to the entity itself (e.g. <code>setStatusAccepted()</code> or <code>setStatus(SubmissionStatus::ACCEPTED_SLUG)</code>) because I can't look up the referred entity from there.</p>
  
  <p>An injected service which does a lookup on creation and stores references. Its usage would be e.g. <code>$entity-&gt;setStatus($statusRef-&gt;accepted)</code>, <code>$entity-&gt;setStatus($statusRef-&gt;get(SubmissionStatus::ACCEPTED_SLUG))</code>. The first option breaks IDE code completion. The second one is a bit too long, even though shorter than <code>$em-&gt;getRepository-&gt;find</code></p>