Статьи

Исправления данных в Symfony2

Когда я впервые начал изучать Symfony (1.x) с его проектом Jobeet, я думал, что возможность загружать кучу тестовых данных в базу данных очень полезна.

В этой статье мы вернемся к этой функции, которая была полностью перемоделирована и поэтому многому нас может научить.

подготовка

В этой статье у нас будет две сторонние библиотеки для дальнейшего расширения возможностей Symfony.

Первый — это DoctrineFixturesBundle , используемый для загрузки тестовых данных с помощью Doctrine ORM , который является стандартным ORM в Symfony.

Пожалуйста, следуйте инструкциям в официальной документации Symfony для установки и настройки DoctrineFixturesBundle. Если вы знакомы с Composer, процесс должен быть простым и понятным, никаких хлопот.

Затем мы установим PHPUnit, стандартную среду тестирования, используемую Symfony. Его официальный сайт предлагает загрузить последнюю phpunit.phar файла phpunit.phar . Просто сохраните этот файл в корневой каталог Symfony, и все готово. Symfony поставляется со стандартным и работоспособным файлом конфигурации PHPUnit ( app/phpunit.xml.dist ). В обычных условиях мы оставим этот файл без изменений, и PHPUnit будет работать нормально. Мы будем использовать PHPUnit в продолжении этой статьи, поэтому просто убедитесь, что он у вас есть.

Конечно, чтобы две вышеупомянутые библиотеки работали в нашем приложении, нам нужно сначала настроить проект / приложение Symfony. Эта тема освещена в моей предыдущей статье «Создание веб-приложения с помощью Symfony 2: начальная загрузка» . Пожалуйста, посмотрите, если вы еще не знакомы с настройкой Symfony 2. На самом деле, эта статья довольно тесно связана с приложением Book Collection, которое мы создали ранее (хотя и с некоторыми изменениями в схеме базы данных).

Написание нашего первого файла фиксации данных

Поскольку мы создаем приложение для сбора книг, нам нужно несколько таблиц для описания связей между информацией, связанной с книгой:

  • book_book : содержит информацию о книге, включая идентификатор книги, название, автора, ISBN, издателя (FK для другой таблицы book_publisher ), место покупки (FK для другой таблицы book_place ), дату покупки и т. д.
  • book_place , book_publisher : содержит информацию о месте покупки и издателе соответственно.
  • book_taglist : содержит тег для книги и формирует отношение «многие ко многим» к таблице book_book .
  • book_headline , book_review : содержит мои мысли о чтении определенной книги. Заголовок является основной таблицей и формирует отношение один-ко-многим с book_review , отношение один-к-одному с book_book .
  • Другие таблицы для сбора дополнительной информации.

В этом приложении я настраиваю базу данных ( rsywx_test ) со следующей структурой. Схема рисуется с помощью этого онлайн-инструмента .

ПРИМЕЧАНИЕ . Я перечислил не все таблицы / поля на этом чертеже схемы, а только эти первичные ключи и внешние ключи, чтобы проиллюстрировать отношения между таблицами. Полный дамп SQL можно найти в репозитории Github, созданном для этой статьи.

Для начала мы создадим book_place данных для book_place .

 <? php namespace  tr\rsywxBundle\DataFixtures\ORM ; 

 use  \Doctrine\Common\DataFixtures\AbstractFixture ; 
 use  \Doctrine\Common\DataFixtures\OrderedFixtureInterface ; 
 use  \Doctrine\Common\Persistence\ObjectManager ; 
 use  \tr\rsywxBundle\Entity\BookPlace as   BookPlace ; 

 class   LoadPlaceData   extends   AbstractFixture   implements   OrderedFixtureInterface 
 { 
     /** * * {@inheritDoc} */ 
     public   function  load ( ObjectManager  $manager ) 
     { 
         //Create a common place $place1 = new   BookPlace (); $place1 -> setName ( 'Common' ); $this -> addReference ( 'commonPlace' ,  $place1 ); 

         //Create a special place $place2 = new   BookPlace (); $place2 -> setName ( 'Special' ); $this -> addReference ( 'specialPlace' ,  $place2 ); $manager -> persist ( $place1 ); $manager -> persist ( $place2 ); $manager -> flush (); 
     } 

     /** * * {@inheritDoc} */ 
     public   function  getOrder () 
     { 
        return   2 ;  
     } 
 } 

Несколько вещей, чтобы выделить здесь:

  1. Есть 4 заявления об use . Первые 3 являются стандартными и требуются почти для всех файлов данных. Последнее use состоит в том, чтобы включить пространство имен для таблицы book_place . Это должно соответствовать таблице, в которую мы должны загрузить данные.
  2. Файл OrderedFixtureInterface данных будет содержать настраиваемый класс (происходит от AbstractFixture и реализует OrderedFixtureInterface ) и содержит по крайней мере две функции: load (для загрузки данных примера) и getOrder (для указания порядка загрузки и сохранения целостности ссылок среди PK / FK). ).
  3. В функции load мы создаем несколько экземпляров book_place и присваиваем значения для name . Поле id не должно быть назначено, так как это поле с автоинкрементом.
  4. Кроме того, после создания объекта book_place мы добавляем ссылку на этот объект с помощью $this->addReference() . Это ОБЯЗАТЕЛЬНО, так как нам потребуется book_book ссылка на этот объект при book_book данных таблицы book_book . Возможно, вы помните, что в таблице book_book ее полем является поле id таблицы FK to book_place .
  5. Наконец, мы сохраняем объект и делаем физическую вставку в таблицу.
  6. В функции getOrder мы явно указываем, что этот файл getOrder должен быть загружен на втором месте (только в моем случае после book_publisher ).

Этот файл данных должен быть помещен в your project root/src/your bundle namespace/DataFixtures/ORM . В моей бродячей установке Symfony каталог является /www/rsywx/src/tr/rsywxBundle/DataFixtures/ORM а имя файла — LoadPlace.php .

Теперь мы можем загрузить файл данных:

php app / console доктрина: светильники: нагрузка

Эта консольная команда Symfony захватит все файлы, находящиеся в указанном выше каталоге, и начнет вставлять образцы данных в порядке, указанном функцией getOrder() каждого файла.

Выполнено! Вернитесь к своему любимому инструменту администрирования MySQL и сделайте select * from book_place или тому подобное, мы увидим, что две записи были вставлены в таблицу book_place .

Загрузить данные книги и ссылки на другие объекты

Далее, давайте загрузим данные книги. Этот файл фиксации будет немного сложнее, так как он будет использовать цикл для создания множества объектов книги, а также использовать ранее созданную ссылку на объект, чтобы установить значение для внешних ключей.

Выдержка из LoadBook.php выглядит следующим образом:

 <? php namespace  tr\rsywxBundle\DataFixtures\ORM ; 

 ... 
 use  \tr\rsywxBundle\Entity\BookBook as   BookBook ; 

 class   LoadBookData   extends   AbstractFixture   implements   OrderedFixtureInterface 
 { 

     /** * * {@inheritDoc} */ 
     public   function  load ( ObjectManager  $manager ) 
     { 
         //Now we create a 100 general book 
         for   ( $i =   1 ;  $i <=   100 ;  $i ++) 
         { $p =   new   BookBook (); $p -> setAuthor ( 'Normal' ); 
 ... $p -> setPurchdate ( new  \DateTime ()); $p -> setPubdate ( new  \DateTime ()); 
 ... $p -> setPage ( $i ); 
 ... $p -> setPublisher ( $this -> getReference ( 'commonPub' )); $p -> setPlace ( $this -> getReference ( 'commonPlace' )); $manager -> persist ( $p ); 
         } 

         //Create a special book $s =   new   BookBook (); 
 ... $s -> setPurchdate ( new  \DateTime ( '1970-1-1' )); $s -> setPubdate ( new  \DateTime ( '1970-1-1' )); $s -> setPrintdate ( new  \DateTime ( '1970-1-1' )); 
 ... $this -> addReference ( 'aBook' ,  $s ); $manager -> persist ( $s ); $manager -> flush (); 

     } 

     /** * * {@inheritDoc} */ 
     public   function  getOrder () 
     { 
         return   3 ; 
     } 

 } 

В этом файле фикстера мы создали в общей сложности 101 запись. Один из них является особенным, так как он создается в самом конце (таким образом, имеет наибольший id ) и его поля, связанные с датой, установлены в EPOCH.

Обратите внимание, как мы устанавливаем поля FK ( publisher и place ). Поскольку эти два поля являются FK для других таблиц, мы не можем просто присвоить целое число этим двум. Нам нужно будет использовать $this->getReference() для захвата ранее созданных объектов. И да! Вот почему нам нужен getOrder() чтобы указать последовательность загрузки, т. getOrder() Таблица книг должна быть загружена после таблицы издателя и таблицы размещения.

Кроме того, мы создали ссылку на мою «Специальную книгу», чтобы этот объект можно было использовать позже для других таблиц ( book_headline , book_taglist ).

В моем приложении всего 6 файлов данных. После загрузки этих 6 файлов база данных для моего приложения более или менее готова. После некоторой кодировки в Controllers, Views, сайт будет работать:

С примерами данных сайт становится более значимым и может помочь в нашей дальнейшей настройке и функциональном тестировании.

У нас есть много функций на этой странице индекса, которая не рассматривается в этой статье. Например, есть два виджета Dart, показывающие QOTD (цитата дня) и информацию о местной погоде. Это описано в моих предыдущих статьях в Sitepoint ( часть 1 и часть 2 ).

Мы почти закончили работу с Symfony DataFixturesBundle, за исключением еще одной вещи. Вспомните мою статью об уникальном индексе ? В некотором смысле, я категорически против использования целочисленных полей с автоинкрементом в качестве первичного ключа таблицы. Также как перфекционист, когда я создаю значимый уникальный индекс, я просто исключаю существование другого автоматически увеличиваемого целочисленного поля и использую этот интерфейс в качестве моего PK.

Но чтобы соблюсти ограничение пакета данных, необходимо сделать один компромисс. Symfony DataFixturesBundle говорит:

Должно быть автоматически увеличенное целочисленное поле в качестве PK для установления ссылки на связь

Конечно, вы можете создать пользовательский интерфейс и другие индексы, если хотите.

Итак, в моей таблице book_book , хотя у меня есть самодостаточное поле ( book_id в форме, например «12345», строка для обозначения моих книг), я должен создать другое поле ( id , автоинкрементное целое число), чтобы действовать как ПК!

Если мы пропустим этот шаг, сама структура базы данных все еще действительна, и все отношения FK / PK все еще могут быть установлены. Тем не менее, будет по крайней мере одна странная и трудная для понимания ошибка: $this->getReference() в любой таблице, которая ссылается на book_book (без автоматически увеличиваемого целочисленного поля как PK), завершится ошибкой, вызывая ошибку «Неопределенный индекс» ,

Вывод

В этой статье мы рассмотрели Data Fixtures и правильный способ предоставления образцов данных для нашего приложения через Doctrine. В следующей статье, выходящей в следующий понедельник, мы пройдем функциональное тестирование и будем использовать данные, которые мы здесь увидели.

Не стесняйтесь комментировать, и мы будем рады более подробно рассмотреть эту тему, если вы заинтересованы.