
Я намагався написати цей посібник по Doctrine 2 ORM протягом тривалого часу, але ніяк не міг взятися за це. Нарешті, я зібрався з думками і зробив це. Тож я ділюся кількома техніками роботи з Doctrine2 ORM, які допоможуть покращити продуктивність сайту на Symfony2(точніше, будь-якого сайту, що використовує Doctrine2 ORM). Я створив проект і виклав його на GitHub як візуальний посібник, щоб кожен міг перевірити мої слова на ділі.
1. Завантаження всіх необхідних зв'язків
Я вважаю, що цей метод є найефективнішим. Ось чому він займає перше місце.
Припустимо, у нас є 5 авторів і їх 100 постів, і нам потрібно відобразити всі пости з іменами авторів на цій сторінці.
Шаблон:
{% for post in posts %} <article><h2>{{ post.title }}</h2><p>Автор: {{ post.author.firstName }} {{ post.author.lastName }} створено {{ post.createdAt | date('d-m-Y H:i') }}</p><p>{{ post.text }}</p></article> {% endfor %}
Що може бути простіше, скажете ви, і зробите щось на кшталт цього:
$posts=$this->getDoctrine()->getRepository('AcmeDemoBundle:Post')->findAll();
Але якщо ви подивитеся на профайлер (Symfony Profiler Toolbar), ви побачите цю тривожну інформацію:
Звідки стільки запитів до бази даних, якщо ми зробили лише один запит?
Справа в тому, що Doctrine не завантажує за замовчуванням зв'язки сутностей, і коли ми звертаємося до них, видається новий запит за цими даними. Ось чому у нас є ще 5 запитів для авторів постів.
Щоб оптимізувати запит Doctrine 2 ORM, нам потрібно використовувати JOIN і додати необхідні сутності до вибірки. Ось оптимізований код методу репозиторію для отримання списку постів.
/** * Знайти всі пости з авторами * * @return array | Post[] */publicfunction findAllPostsAndAuthors(){$qb=$this->createQueryBuilder('p');$qb->addSelect('a')->innerJoin('p.author','a'); return$qb->getQuery()->getResult();}
Використовуючи цей метод для отримання списку постів, ми отримаємо таку картину в профайлері:
Тепер у нас залишився лише один запит. Час виконання його зменшився в порівнянні з попередньою версією.
2. Оновлення кількох сутностей за запитом
Припустимо, тепер нам потрібно оновити дату створення всіх наших постів. Часто це робиться шляхом отримання всіх постів з бази даних (згідно з документацією Doctrine 2) і потім оновлення кожної сутності Doctrine 2 окремо в циклі:
$newCreatedAt=new \DateTime();$posts=$this->getDoctrine()->getRepository('AcmeDemoBundle:Post')->findAll(); /** @var Post $post */foreach($postsas$post){$post->setCreatedAt($newCreatedAt);}$this->getDoctrine()->getManager()->flush();
В результаті, ми маємо наступні значення в профайлері після оновлення 100 постів:
Як ми можемо побачити, до бази даних було направлено аж 103 запити. Навіть профайлер виділив їх жовтим кольором. Це вже має вас насторожити і змусити задуматися про те, як підвищити продуктивність сайту Symfony2.
Чому так багато запитів? Оскільки ми оновлювали сутності в циклі, ми отримали велику кількість запитів UPDATE до бази даних.
Для оптимізації нам потрібно написати лише один запит для оновлення всіх записів у базі даних.
/** * Оновити дату створення для всіх постів * * @param \DateTime $newCreatedAt * * @return int */publicfunction updateCreatedAtForAllPosts(\DateTime $newCreatedAt){$qb=$this->createQueryBuilder('p');$qb->update()->set('p.createdAt',':newCreatedAt')->setParameter('newCreatedAt',$newCreatedAt); return$qb->getQuery()->execute();}
Після цього ми отримаємо лише один запит до бази даних і також зменшимо час виконання.
3. Відмова від гідратації Doctrine 2
Що таке гідратація? Гідратація — це перетворення масиву в об'єкт і назад.
Гідратація є найвитратнішою за часом і пам'яттю ORM.
Саме тому, коли ми отримуємо велику кількість даних з бази даних лише для відображення, наприклад, при отриманні списку, гідратація всього в об'єкти сутностей буде дуже затратною. Краще отримати дані у вигляді асоціативного масиву та зекономити ресурси.
$posts=$this->createQueryBuilder('p')->getQuery()->getArrayResult();
4. Використання проксі-ссилок
Припустимо, нам потрібно додати зв'язок з автором до поста, і в нас є ID автора. В більшості випадків це робиться шляхом отримання сутності автора з бази даних за його ID, а потім простим встановленням зв'язку з постом автора:
$author=$this->getDoctrine()->getRepository('AcmeDemoBundle:Author')->find($autorId); $post=new Post();$post->setAuthor($author);
Як виявилось, отримання автора з бази даних є зайвим запитом, і його дуже легко відкинути. Для цього є проксі-ссилки Doctrine2. Ось як це працює:
$em=$this->getDoctrine()->getManager(); $post=new Post();$post->setAuthor($em->getReference('Acme\DemoBundle\Entity\Author',$authorId));
Таким чином, ми позбулися зайвого запиту і встановили зв'язок лише з ID автора.
5. Використання панелі профайлера Symfony
Хоча це останній пункт, цей рада не є менш важливою. Постійний контроль за тим, що відбувається в профайлері, безумовно, допоможе вам розробити ефективні проекти на Symfony2. Завдяки профайлеру ви завжди будете відстежувати свої запити до бази даних і зможете оптимізувати їх вчасно для підвищення продуктивності сайту в Symfony 2.
Дізнатися більше про архітектуру Doctrine 2 ви можете тут.
Stfalcon.com запрошує вас до співпраці!