Проект на Github
Узнать как установить нужную вам часть руководства, можно в описании к репозиторию по ссылке. (Например, если вы хотите начать с это урока не проходя предыдущий)


Домашняя страница


Мы начнём эту часть с создания домашней страницы. В обычном блоге записи обычно отсортированы от новых к старым. Запись целиком будет доступна по ссылке со страницы блога. Так как мы уже создали маршрут, контроллер и отображение для домашней страницы мы можем легко их обновить.

Получение записей. Выполнение запросов к базе данных.


Чтобы отобразить записи блога нам необходимо их извлечь из базы данных. Doctrine 2 предоставляет нам Doctrine Query Language (DQL) и QueryBuilder для достижения этой цели (Вы можете также сделать обычный SQL запрос через Doctrine 2, но такой метод не рекомендуется, поскольку это уводит нас от абстракции базы данных которую предоставляет нам Doctrine 2. Мы будем использовать QueryBuilder, поскольку он обеспечивает хороший объектно-ориентированный способ для нас, чтобы сгенерировать DQL, который мы можем использовать для запросов к базе. Давайте обновим метод indexAction в контроллере Page расположенного src/Blogger/BlogBundle/Controller/PageController.php для получения записей из базы данных.
Вставьте следующий код
// src/Blogger/BlogBundle/Controller/PageController.php
class PageController extends Controller
{
    public function indexAction()
    {
        $em = $this->getDoctrine()
                   ->getManager();

        $blogs = $em->createQueryBuilder()
                    ->select('b')
                    ->from('BloggerBlogBundle:Blog',  'b')
                    ->addOrderBy('b.created', 'DESC')
                    ->getQuery()
                    ->getResult();

        return $this->render('BloggerBlogBundle:Page:index.html.twig', array(
            'blogs' => $blogs
        ));
    }

    // ..
}


Мы начали с получения экземпляра QueryBuilder из Manager. Это позволит начать создавать запрос используя множество методов, которые предоставляет нам QueryBuilder. Полный список доступных методов можно посмотреть в документации к QueryBuilder. Лучше всего начать с вспомогательных методов. Это такие методы как select(), from() и addOrderBy(). Как и в случае предыдущих взаимодействий с Doctrine 2, мы можем использовать короткие аннотации для обращения к сущности Blog через BloggerBlogBundle:Blog (учтите, это тоже самое что и Blogger\BlogBundle\Entity\Blog). Когда мы закончили, указывать критерии для запроса, мы вызываем метод getQuery(), который возвращает экземпляр DQL. Мы не можем получить результаты из объекта QueryBuilder, мы всегда должны сначала преобразовать это в экземпляр DQL. Экземпляр объекта DQL предоставляет нам метод getResult() который возвращает коллекцию записей Блога. Позже мы увидим, что экземпляр объекта DQL имеет целый ряд методов для возврата результатов, включая getSingleResult() и getArrayResult().

Отображение


Теперь у нас есть коллекция записей и нам нужно отобразить их. Замените контент домашней страницы, расположенной src/Blogger/BlogBundle/Resources/views/Page/index.html.twig
следующим
{# src/Blogger/BlogBundle/Resources/views/Page/index.html.twig #}
{% extends 'BloggerBlogBundle::layout.html.twig' %}

{% block body %}
    {% for blog in blogs %}
        <article class="blog">
            <div class="date"><time datetime="{{ blog.created|date('c') }}">{{ blog.created|date('l, F j, Y') }}</time></div>
            <header>
                <h2><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">{{ blog.title }}</a></h2>
            </header>

            <img src="{{ asset(['images/', blog.image]|join) }}" />
            <div class="snippet">
                <p>{{ blog.blog(500) }}</p>
                <p class="continue"><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">Continue reading...</a></p>
            </div>

            <footer class="meta">
                <p>Comments: -</p>
                <p>Posted by <span class="highlight">{{blog.author}}</span> at {{ blog.created|date('h:iA') }}</p>
                <p>Tags: <span class="highlight">{{ blog.tags }}</span></p>
            </footer>
        </article>
    {% else %}
        <p>There are no blog entries for symblog</p>
    {% endfor %}
{% endblock %}



Мы ввели одну из управляющих структур Twig, for..else..endfor. Если вы ранее не использовали шаблонизаторы, вероятно вам будет знаком следующий PHP код.
<?php if (count($blogs)): ?>
    <?php foreach ($blogs as $blog): ?>
        <h1><?php echo $blog->getTitle() ?><?h1>
        <!-- rest of content -->
    <?php endforeach ?>
<?php else: ?>
    <p>There are no blog entries</p>
<?php endif ?>


Управляющая структура Twig for..else..endfor представляет собой более простой способ достижения этой задачи. Большая часть кода в шаблоне домашней страницы касается вывода информации блога в формате HTML. Однако, здесь есть несколько моментов которые нам нужно учесть. Во-первых, мы используем Twig path функцию для создания маршрутов. Так как страница блога требует ID записи переданной в URL, мы должны вставить его в качестве аргумента в функцию path. Это можно увидеть в этом фрагменте кода:
<h2><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">{{ blog.title }}</a></h2>


Во-вторых мы возвращаем контент используя
<p>{{blog.blog(500) }}</p> 
Число 500 является максимальной длиной поста который мы хотим получить обратно из функции. Для этого нам необходимо обновить метод getBlog, который Doctrine 2 сгенерировало для нас ранее. Обновите метод getBlog в сущности Blog, расположенный
 src/Blogger/BlogBundle/Entity/Blog.php 

// src/Blogger/BlogBundle/Entity/Blog.php
public function getBlog($length = null)
{
    if (false === is_null($length) && $length > 0)
        return substr($this->blog, 0, $length);
    else
        return $this->blog;
}


Так как метод getBlog должен вернуть весь пост в блоге, мы установим параметр $length который будет иметь значение по умолчанию null. Если передается значение null, возвращается вся запись.
Теперь, если вы введете в ваш браузер localhost:8000 вы должны увидеть домашнюю страницу, отображающую последние записи в блоге. У вас также должна быть возможность перейти к полной записи блога, нажав на название записи или на ссылку "Продолжить чтение… (Continue reading)".



В то время как мы можем запросить записи в контроллере, это не самое лучшее место для этого. Запрос будет лучше разместить за пределами контроллера из-за целого ряда причин:
Мы не сможем повторно использовать запрос в другом месте приложения, без дублирования кода QueryBuilder.
Если бы мы продублировали код QueryBuilder, мы должны были бы сделать несколько изменений в будущем, если запрос нуждался бы в изменении.
Разделение запроса и контроллера позволит нам протестировать запрос независимо от контроллера.
Doctrine 2 предоставляет классы Репозитория для облегчения этой задачи.

Репозитории Doctrine 2


Мы уже немного поговорили о классах Репозитория Doctrine 2 в предыдущей главе, когда мы создавали страницу блога. Мы использовали реализацию класса по умолчанию Doctrine\ORM\EntityRepository для извлечения записи блога из базы данных с помощью метода find(). Так как нам нужен пользовательский запрос к базе, нам нужно создать пользовательский Репозиторий. Doctrine 2 может помочь в решении этой задачи. Обновите метаданные сущности Blog в файле src/Blogger/BlogBundle/Entity/Blog.php
// src/Blogger/BlogBundle/Entity/Blog.php
/**
 * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\BlogRepository")
 * @ORM\Table(name="blog")
 * @ORM\HasLifecycleCallbacks()
 */
class Blog
{
    // ..
}


Вы можете видеть, что мы определили пространство имен для класса BlogRepository с которым эта сущность ассоциирована. Так как мы обновили метаданные Doctrine 2 для сущности Blog, нам необходимо повторно запустить команду doctrine:generate:entities следующим образом.
$ php app/console doctrine:generate:entities Blogger\BlogBundle

Doctrine 2 создаст оболочку класса BlogRepository, расположенного src/Blogger/BlogBundle/Entity/Repository/BlogRepository.php

<?php

namespace Blogger\BlogBundle\Entity\Repository;

/**
 * BlogRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class BlogRepository extends \Doctrine\ORM\EntityRepository
{
}



Класс BlogRepository расширяет класс EntityRepository который предоставляет метод find(), который мы использовали ранее. Давайте обновим класс BlogRepository, переместив в него код QueryBuilder из контроллера Page.

<?php

namespace Blogger\BlogBundle\Entity\Repository;

/**
 * BlogRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class BlogRepository extends \Doctrine\ORM\EntityRepository
{
    public function getLatestBlogs($limit = null)
    {
        $qb = $this->createQueryBuilder('b')
            ->select('b')
            ->addOrderBy('b.created', 'DESC');

        if (false === is_null($limit))
            $qb->setMaxResults($limit);

        return $qb->getQuery()
            ->getResult();
    }
}



Мы создали метод getLatestBlogs который будет возвращать последние записи в блоге, таким же образом как код QueryBuilder делал это в контроллере. В классе репозитория мы имеем прямой доступ к QueryBuilder с помощью метода createQueryBuilder(). Мы также добавили параметр по умолчанию $limit, таким образом мы можем ограничить количество возвращаемых результатов. Результатом запроса будет то же, что было в случае с контроллером. Вы, возможно, заметили, что нам нет нужды указывать объект с помощью метода from(). Это связано с тем, что мы находимся в BlogRepository, который связан с сущностью Blog. Если мы посмотрим на реализацию метода createQueryBuilder в классе EntityRepository мы можем увидеть, что метод from() был вызван для нас.
// Doctrine\ORM\EntityRepository
public function createQueryBuilder($alias, $indexBy = null)
    {
        return $this->_em->createQueryBuilder()
            ->select($alias)
            ->from($this->_entityName, $alias, $indexBy);
    }

Наконец давайте обновим метод indexAction в контроллере Page для использования BlogRepository.
// src/Blogger/BlogBundle/Controller/PageController.php
class PageController extends Controller
{
    public function indexAction()
    {
        $em = $this->getDoctrine()
                   ->getManager();

        $blogs = $em->getRepository('BloggerBlogBundle:Blog')
                    ->getLatestBlogs();

        return $this->render('BloggerBlogBundle:Page:index.html.twig', array(
            'blogs' => $blogs
        ));
    }

    // ..
}

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

Подробнее о модели: Создание сущности Comment


Записи — это только половина истории, когда речь идет о ведении блога. Мы также должны позволить читателям возможность комментировать записи в блоге. Эти комментарии также должны быть сохранены и связаны с сущностью Blog так как запись может содержать много комментариев.
Мы начнем с определения основы, класса сущности Comment. Создайте новый файл, расположенный в src/Blogger/BlogBundle/Entity/Comment.php и вставьте
следующее
<?php
// src/Blogger/BlogBundle/Entity/Comment.php

namespace Blogger\BlogBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\CommentRepository")
 * @ORM\Table(name="comment")
 * @ORM\HasLifecycleCallbacks
 */
class Comment
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string")
     */
    protected $user;

    /**
     * @ORM\Column(type="text")
     */
    protected $comment;

    /**
     * @ORM\Column(type="boolean")
     */
    protected $approved;

    /**
     * @ORM\ManyToOne(targetEntity="Blog", inversedBy="comments")
     * @ORM\JoinColumn(name="blog_id", referencedColumnName="id")
     */
    protected $blog;

    /**
     * @ORM\Column(type="datetime")
     */
    protected $created;

    /**
     * @ORM\Column(type="datetime")
     */
    protected $updated;

    public function __construct()
    {
        $this->setCreated(new \DateTime());
        $this->setUpdated(new \DateTime());

        $this->setApproved(true);
    }

    /**
     * @ORM\preUpdate
     */
    public function setUpdatedValue()
    {
       $this->setUpdated(new \DateTime());
    }
}


Большую часть того, что вы здесь видите, мы уже рассмотрели в предыдущей части, однако мы использовали метаданные для создания ссылки на сущность Blog. Так как комментарий относится к записи, мы установили ссылку в сущности Comment к сущности Blog к которой она принадлежит. Мы сделали это указав ссылку ManyToOne к сущности Blog. Мы также указали, что обратная связь для этой ссылки будет доступна через комментарии. Чтобы инвертировать, нам нужно обновить сущность Blog так Doctrine 2 будет знать, что запись может содержать много комментариев. Обновите сущность Blog src/Blogger/BlogBundle/Entity/Blog.php
Вставьте код
<?php
// src/Blogger/BlogBundle/Entity/Blog.php

namespace Blogger\BlogBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\BlogRepository")
 * @ORM\Table(name="blog")
 * @ORM\HasLifecycleCallbacks
 */
class Blog
{
    // ..

    /**
     * @ORM\OneToMany(targetEntity="Comment", mappedBy="blog")
     */
    protected $comments;

    // ..

    public function __construct()
    {
        $this->comments = new ArrayCollection();

        $this->setCreated(new \DateTime());
        $this->setUpdated(new \DateTime());
    }

    // ..
}


Есть несколько изменений на которые нужно указать. Во-первых, мы добавили метаданные к объекту $comments. Помните, в предыдущей главе мы не добавляли метаданные для этого объекта, потому что мы не хотели, чтобы Doctrine 2 его сохраняла? Это по-прежнему так, однако, мы хотим, чтобы Doctrine 2, имела возможность заполнить этот объект соответствующими записями Comment. То есть то, что позволяют достичь метаданные. Во-вторых, Doctrine 2 требует, чтобы мы установили значение по умолчанию для объекта $comments в ArrayCollection. Мы сделаем это в конструкторе. Кроме того, обратите внимание на заявление use импортирующее класс ArrayCollection.

Так как мы создали сущность Comment и обновили сущность Blog, давайте создадим методы доступа. Выполните следующую команду Doctrine 2.
$ php app/console doctrine:generate:entities Blogger\BlogBundle


Обе сущности будут обновлены с корректными методами доступа. Вы также заметите, что в
src/Blogger/BlogBundle/Entity/Repository/CommentRepository.php

был создан класс CommentRepository, так как мы указали это в метаданных.

Наконец, мы должны обновить базу данных, чтобы отразить изменения в наших сущностях. Мы могли бы воспользоваться командой doctrine:schema:update которая показана ниже, чтобы сделать это, но вместо этого мы расскажем о Миграциях Doctrine 2.
$ php app/console doctrine:schema:update --force

Миграции Doctrine 2


Расширение Миграций Doctrine 2 и бандл не поставляется с Symfony2, мы должны вручную их установить. Откройте файл composer.json расположенный в корне проекта и вставьте зависимости Миграций Doctrine 2 и бандл как показано ниже.
"require": {
    // ...
    "doctrine/doctrine-migrations-bundle": "dev-master",
    "doctrine/migrations": "dev-master"
}

Далее обновите библиотеки командой.
$ composer update

Это обновит все библиотеки с Github и установит их в необходимые директории.
Теперь давайте зарегистрируем бандл в kernel расположенного в app/AppKernel.php
// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
        // ...
    );
    // ...
}


Теперь мы готовы обновить базу данных чтобы отразить изменения в сущности, этот процесс, пройдёт в 2 этапа. Во-первых, мы должны поручить Миграциям Doctrine 2 поработать с различиями между сущностями и текущей схемой базы данных. Это делается командой doctrine:migrations:diff. Во-вторых, мы должны выполнить миграцию, основанную на данных созданных первой командой. Это делается командой doctrine:migrations:migrate.
Выполните следующие 2 команды для того чтобы обновить схему базы данных.
$ php app/console doctrine:migrations:diff

$ php app/console doctrine:migrations:migrate

На предупреждение, показанное ниже отвечаем yes.
WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n): yes

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

Заметка

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


Совет

Миграции Doctrine 2 являются отличным способом для обновления базы данных на production, поскольку изменения можно сделать программно. Это означает, что мы можем интегрировать эту задачу в сценарий развертывания проекта, поэтому база данных обновится автоматически при развертывании новой версии приложения. Миграции Doctrine 2 также позволяют откатить изменения, так как каждая миграция имеет up и down метод. Чтобы откатиться к предыдущей версии вам необходимо указать номер версии, на которую вы хотели бы вернуться, сделать это можно, так как показано ниже.
$ php app/console doctrine:migrations:migrate 20110806183439


Фикстуры данных



Теперь у нас есть сущность Comment, давайте добавим Фикстуры данных. Это хороший момент, в то время, когда вы создаете сущность. Мы знаем, что комментарий должен иметь связь с сущностью Blog, как мы указали это в метаданных, поэтому при создании фикстур для сущностей Comment нам нужно будет указать сущность Blog. Мы уже создали фикстуры для сущности Blog таким образом, мы могли бы просто обновить этот файл, чтобы добавить сущности Comment. Это может быть управляемым сейчас, но что произойдёт, когда мы позже добавим пользователей и другие сущности в наш бандл? Лучше всего будет создать новый файл для фикстур сущности Comment. Проблемой в этом подходе является то как мы получим доступ к записям Blog из фикстур блога.

К счастью, это может быть легко достигнуто путем добавления ссылки на объекты в файле фикстур к которому другие файлы фикстур имеют доступ. Обновите Фикстуры данных сущности Blog расположенной src/Blogger/BlogBundle/DataFixtures/ORM/BlogFixtures.php
следующим
<?php
// src/Blogger/BlogBundle/DataFixtures/ORM/BlogFixtures.php

namespace Blogger\BlogBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Blogger\BlogBundle\Entity\Blog;

class BlogFixtures extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        // ..

        $manager->flush();

        $this->addReference('blog-1', $blog1);
        $this->addReference('blog-2', $blog2);
        $this->addReference('blog-3', $blog3);
        $this->addReference('blog-4', $blog4);
        $this->addReference('blog-5', $blog5);
    }

    public function getOrder()
    {
        return 1;
    }
}


Изменения, которые здесь стоит отметить, расширение класса AbstractFixture и реализация OrderedFixtureInterface. Также обратите внимание на 2 новых use оператора импортирующие эти классы.

Мы добавляем ссылки на сущности блог с помощью метода addReference(). Этот первый параметр является идентификатором ссылки, которую мы можем использовать для извлечения объекта позже. В конце мы должны реализовать метод getOrder() чтобы указать порядок загрузки фикстур. Записи должны быть загружены до комментариев поэтому мы возвращаем 1.

Фикстуры Comment



Теперь мы готовы определить фикстуры для сущности Comment. Создайте файл фикстур src/Blogger/BlogBundle/DataFixtures/ORM/CommentFixtures.php
и вставьте
следующее
<?php
// src/Blogger/BlogBundle/DataFixtures/ORM/CommentFixtures.php

namespace Blogger\BlogBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Blogger\BlogBundle\Entity\Comment;
use Blogger\BlogBundle\Entity\Blog;

class CommentFixtures extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $comment = new Comment();
        $comment->setUser('symfony');
        $comment->setComment('To make a long story short. You can\'t go wrong by choosing Symfony! And no one has ever been fired for using Symfony.');
        $comment->setBlog($manager->merge($this->getReference('blog-1')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('David');
        $comment->setComment('To make a long story short. Choosing a framework must not be taken lightly; it is a long-term commitment. Make sure that you make the right selection!');
        $comment->setBlog($manager->merge($this->getReference('blog-1')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Dade');
        $comment->setComment('Anything else, mom? You want me to mow the lawn? Oops! I forgot, New York, No grass.');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Kate');
        $comment->setComment('Are you challenging me? ');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 06:15:20"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Dade');
        $comment->setComment('Name your stakes.');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 06:18:35"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Kate');
        $comment->setComment('If I win, you become my slave.');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 06:22:53"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Dade');
        $comment->setComment('Your SLAVE?');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 06:25:15"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Kate');
        $comment->setComment('You wish! You\'ll do shitwork, scan, crack copyrights...');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 06:46:08"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Dade');
        $comment->setComment('And if I win?');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 10:22:46"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Kate');
        $comment->setComment('Make it my first-born!');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-23 11:08:08"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Dade');
        $comment->setComment('Make it our first-date!');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-24 18:56:01"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Kate');
        $comment->setComment('I don\'t DO dates. But I don\'t lose either, so you\'re on!');
        $comment->setBlog($manager->merge($this->getReference('blog-2')));
        $comment->setCreated(new \DateTime("2011-07-25 22:28:42"));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Stanley');
        $comment->setComment('It\'s not gonna end like this.');
        $comment->setBlog($manager->merge($this->getReference('blog-3')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Gabriel');
        $comment->setComment('Oh, come on, Stan. Not everything ends the way you think it should. Besides, audiences love happy endings.');
        $comment->setBlog($manager->merge($this->getReference('blog-3')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Mile');
        $comment->setComment('Doesn\'t Bill Gates have something like that?');
        $comment->setBlog($manager->merge($this->getReference('blog-5')));
        $manager->persist($comment);

        $comment = new Comment();
        $comment->setUser('Gary');
        $comment->setComment('Bill Who?');
        $comment->setBlog($manager->merge($this->getReference('blog-5')));
        $manager->persist($comment);

        $manager->flush();
    }

    public function getOrder()
    {
        return 2;
    }
}



C изменениями которые мы сделали в классе BlogFixtures, класс CommentFixtures также расширяет класс AbstractFixture и реализует OrderedFixtureInterface. Это означает, что мы должны также реализовать метод getOrder(). На этот раз мы возвращаем значение 2, обеспечивая этим загрузку фикстур после фикстур записей.

Мы также можем увидеть, как используются ссылки на сущности Blog которые мы создали ранее.
$comment->setBlog($manager->merge($this->getReference('blog-2')));

Теперь мы готовы загрузить фикстуры в базу данных
$ php app/console doctrine:fixtures:load

На предупреждение отвечаем: yes
Careful, database will be purged. Do you want to continue y/N ? yes

Отображение Комментариев


Теперь мы можем отобразить комментарии, связанные с каждым сообщением в блоге. Мы начнём с обновления CommentRepository методом, который получает самые последние одобренные комментарии для блога.

Репозиторий Comment


Откройте класс CommentRepository расположенный src/Blogger/BlogBundle/Entity/Repository/CommentRepository.php и вставьте
следующее
<?php
// src/Blogger/BlogBundle/Entity/Repository/CommentRepository.php

namespace Blogger\BlogBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;

/**
 * CommentRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class CommentRepository extends EntityRepository
{
    public function getCommentsForBlog($blogId, $approved = true)
    {
        $qb = $this->createQueryBuilder('c')
                   ->select('c')
                   ->where('c.blog = :blog_id')
                   ->addOrderBy('c.created')
                   ->setParameter('blog_id', $blogId);

        if (false === is_null($approved))
            $qb->andWhere('c.approved = :approved')
               ->setParameter('approved', $approved);

        return $qb->getQuery()
                  ->getResult();
    }
}


Метод, который мы создали будет получать комментарии к записи блога. Для этого нам нужно добавить where условие к нашему запросу. Условие where использует именованный параметр, который задается с помощью метода setParameter(). Вы должны всегда использовать параметры вместо того, чтобы устанавливать значения непосредственно в запросе
->where('c.blog = ' . blogId)
В этом примере значение $blogId не будет безопасно и может оставить запрос открытым для атаки SQL injection.

Контроллер Blog


Далее нам нужно обновить showAction метод контроллера Blog для извлечения комментариев. Обновите контроллер Blog src/Blogger/BlogBundle/Controller/BlogController.php
Вставьте
// src/Blogger/BlogBundle/Controller/BlogController.php

public function showAction($id)
{
    // ..

    if (!$blog) {
        throw $this->createNotFoundException('Unable to find Blog post.');
    }

    $comments = $em->getRepository('BloggerBlogBundle:Comment')
                   ->getCommentsForBlog($blog->getId());

    return $this->render('BloggerBlogBundle:Blog:show.html.twig', array(
        'blog'      => $blog,
        'comments'  => $comments
    ));
}


Мы используем новый метод в CommentRepository для получения одобренных комментариев. Коллекция $comments также передается в шаблон.

Шаблон Blog show


Теперь у нас есть список комментариев для блога мы можем обновить шаблон Blog show для показа комментариев. Мы могли бы просто поместить вывод комментариев непосредственно в шаблон Blog show, но так как комментарии имеют свою собственную сущность, было бы лучше, отделить отображение в другой шаблон, и включить в него этот. Это позволит нам повторно использовать шаблон вывода комментариев в любом месте приложения. Обновите шаблон Blog show src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig
вставьте
{# src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig #}

{# .. #}

{% block body %}
    {# .. #}

    <section class="comments" id="comments">
        <section class="previous-comments">
            <h3>Comments</h3>
            {% include 'BloggerBlogBundle:Comment:index.html.twig' with { 'comments': comments } %}
        </section>
    </section>
{% endblock %}


Вы можете увидеть новый тег Twig include. Он будет включать содержимое шаблона BloggerBlogBundle:Comment:index.html.twig. Мы также можем передать любое количество аргументов в шаблон. В этом случае нам нужно пройти через коллекцию Comment сущностей для визуализации.

Шаблон Comment show


BloggerBlogBundle:Comment:index.html.twig который мы включили выше пока не существует, поэтому мы должны создать его. Так как это просто шаблон, нам не нужно, создавать маршрут или контроллер для этого нам нужен только файл шаблона. Создайте новый файл
src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig
и вставьте
следующее
{# src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig #}

{% for comment in comments %}
    <article class="comment {{ cycle(['odd', 'even'], loop.index0) }}" id="comment-{{ comment.id }}">
        <header>
            <p><span class="highlight">{{ comment.user }}</span> commented <time datetime="{{ comment.created|date('c') }}">{{ comment.created|date('l, F j, Y') }}</time></p>
        </header>
        <p>{{ comment.comment }}</p>
    </article>
{% else %}
    <p>There are no comments for this post. Be the first to comment...</p>
{% endfor %}



Как вы можете видеть, мы итерируем коллекцию сущностей Comment и выводим комментарии. Расскажем и о ещё одной хорошей функции Twig, cycle. Эта функция будет перебирать значения в массиве, который вы передаете, во время каждой итерации цикла. Текущее значение итерации цикла получается через специальную переменную loop.index0. Это ведет подсчет итераций цикла, начиная с 0. Есть целый ряд других доступных специальных переменных, когда мы находимся в пределах цикла. Вы можете также заметить установку HTML-ID к article элементу. Это позволит нам позже создать ссылки на созданные комментарии.

Comment show CSS



И наконец, давайте добавим немного CSS, чтобы комментарии выглядели стильно. Обновите стили, расположенные в src/Blogger/BlogBundle/Resouces/public/css/blog.css
/** src/Blogger/BlogBundle/Resorces/public/css/blog.css **/
.comments { clear: both; }
.comments .odd { background: #eee; }
.comments .comment { padding: 20px; }
.comments .comment p { margin-bottom: 0; }
.comments h3 { background: #eee; padding: 10px; font-size: 20px; margin-bottom: 20px; clear: both; }
.comments .previous-comments { margin-bottom: 20px; }


Заметка

Если вы не используете метод символических ссылок для обращения к assets бандла в папке web, вы должны повторно запустить команду установки assets чтобы скопировать изменения.
$ php app/console assets:install web



Если теперь посмотрим на одну из show pages, например, http://localhost:8000/2 вы должны увидеть вывод комментариев к записи.



Добавление комментариев


Последняя часть этой главы будет посвящена расширению функциональности для пользователей, добавление комментариев к записи в блоге. Это станет возможным благодаря форме на странице blog show. Мы уже говорили о создании форм в Symfony 2 когда создавали форму на странице контактов. Вместо того чтобы создавать форму комментария вручную, мы можем использовать Symfony2, чтобы он сделал это за нас. Запустите следующую команду для генерации класса CommentType для сущности Comment.
$ php app/console generate:doctrine:form BloggerBlogBundle:Comment


Вы снова заметите использование сокращения чтобы определить сущность Comment.

Подсказка

Вы возможно, заметили что также доступна команда doctrine:generate:form. Это та же команда названая по-другому.


Команда создала класс CommentType расположенный src/Blogger/BlogBundle/Form/CommentType.php

Посмотреть код
<?php

namespace Blogger\BlogBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CommentType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('user')
            ->add('comment')
            ->add('approved')
            ->add('created', 'datetime')
            ->add('updated', 'datetime')
            ->add('blog')
        ;
    }
    
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Blogger\BlogBundle\Entity\Comment'
        ));
    }
}




Мы уже изучили, что происходит здесь, в предыдущем классе Enquiry Type. Мы могли бы начать с настройки этого класса сейчас, но давайте займемся сначала отображением формы.

Отображение формы комментариев.


Так как мы хотим, чтобы пользователь добавлял комментарии со страницы blog show, мы могли бы создать форму в методе showAction контроллера Blog и вывести форму непосредственно в шаблоне show. Однако было бы лучше отделить этот код, как мы это делали с отображением комментариев. Разница между отображением комментариев и отображением формы комментариев в том, что форма комментария нуждается в обработке, поэтому требуется контроллер.

Маршрут


Нам нужно создать новый маршрут для обработки форм. Добавьте новый маршрут, расположенный src/Blogger/BlogBundle/Resources/config/routing.yml
BloggerBlogBundle_comment_create:
    path:  /comment/{blog_id}
    defaults: { _controller: "BloggerBlogBundle:Comment:create" }
    requirements:
        methods:  POST
        blog_id: \d+


Контроллер


Далее, нам необходимо создать новый CommentControler который мы упомянули выше. Создайте новый файл, расположенный в src/Blogger/BlogBundle/Controller/CommentController.php и вставьте
следующее
<?php
// src/Blogger/BlogBundle/Controller/CommentController.php

namespace Blogger\BlogBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Blogger\BlogBundle\Entity\Comment;
use Blogger\BlogBundle\Form\CommentType;
use Symfony\Component\HttpFoundation\Request;

/**
 * Comment controller.
 */
class CommentController extends Controller
{
    public function newAction($blog_id)
    {
        $blog = $this->getBlog($blog_id);

        $comment = new Comment();
        $comment->setBlog($blog);
        $form   = $this->createForm(CommentType::class, $comment);

        return $this->render('BloggerBlogBundle:Comment:form.html.twig', array(
            'comment' => $comment,
            'form'   => $form->createView()
        ));
    }

    public function createAction(Request $request, $blog_id)
    {
        $blog = $this->getBlog($blog_id);

        $comment  = new Comment();
        $comment->setBlog($blog);
        $form    = $this->createForm(CommentType::class, $comment);
        $form->handleRequest($request);

        if ($form->isValid()) {
            // TODO: Persist the comment entity

            return $this->redirect($this->generateUrl('BloggerBlogBundle_blog_show', array(
                    'id' => $comment->getBlog()->getId())) .
                '#comment-' . $comment->getId()
            );
        }

        return $this->render('BloggerBlogBundle:Comment:create.html.twig', array(
            'comment' => $comment,
            'form'    => $form->createView()
        ));
    }

    protected function getBlog($blog_id)
    {
        $em = $this->getDoctrine()
            ->getManager();

        $blog = $em->getRepository('BloggerBlogBundle:Blog')->find($blog_id);

        if (!$blog) {
            throw $this->createNotFoundException('Unable to find Blog post.');
        }

        return $blog;
    }

}





Мы создали 2 метода в контроллере Comment, один для new и один для create. Метод new связан с отображением формы для комментария, метод create связан с обработкой формы комментария. Хотя это может показаться большим куском кода, здесь нет ничего нового, все было рассказано во второй части, когда мы создавали контактную форму. Однако, прежде чем пойти дальше убедитесь, что вы в полной мере поняли, что происходит в контроллере Comment.

Валидация Формы



Мы не хотим, чтобы у пользователей была возможность оставлять комментарии с пустыми значениями параметров user и comment. Для достижения этого, вспомним Валидацию которую мы рассматривали во второй части при создании формы запроса. Обновите сущность Comment расположенную src/Blogger/BlogBundle/Entity/Comment.php
Посмотреть код
<?php
// src/Blogger/BlogBundle/Entity/Comment.php

// ..

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;

// ..
class Comment
{
    // ..

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('user', new NotBlank(array(
            'message' => 'You must enter your name'
        )));
        $metadata->addPropertyConstraint('comment', new NotBlank(array(
            'message' => 'You must enter a comment'
        )));
    }

    // ..
}


Здесь проверяется заполнены ли поля user и comment. Также мы переопределили сообщения по умолчанию. Не забудьте добавить пространство имен ClassMetadata и NotBlank, как показано выше.

Отображение



Далее нам нужно создать 2 шаблона для методов new и create контроллера. Создайте новый файл, расположенный в src/Blogger/BlogBundle/Resources/views/Comment/form.html.twig и вставьте
следующее
{# src/Blogger/BlogBundle/Resources/views/Comment/form.html.twig #}
{{ form_start(form, { 'action': path('BloggerBlogBundle_comment_create' , { 'blog_id' : comment.blog.id }), 'method': 'POST', 'attr': {'class': 'blogger'} }) }}

    {{ form_widget(form) }}
    <p>
        <input type="submit" value="Submit">
    </p>




Цель этого шаблона довольно простая, он просто отображает форму комментария. Вы также заметите, что метод action формы является POST и относится к новому маршруту, который мы создали BloggerBlogBundle_comment_create.

Теперь давайте создадим шаблон для create метода. Создайте новый файл, расположенный в src/Blogger/BlogBundle/Resources/views/Comment/create.html.twig и вставьте
следующее
{% extends 'BloggerBlogBundle::layout.html.twig' %}

{% block title %}Add Comment{% endblock%}

{% block body %}
    <h1>Add comment for blog post "{{ comment.blog.title }}"</h1>
    {% include 'BloggerBlogBundle:Comment:form.html.twig' with { 'form': form } %}
{% endblock %}



Так как метод createAction контроллера Comment имеет дело с обработкой формы, он также должен быть в состоянии отображать ее, так как там могут возникнуть ошибки. Мы повторно воспользуемся BloggerBlogBundle:Comment:form.html.twig для отображения формы чтобы не дублировать код.

Давайте теперь обновим шаблон blog show для отображения формы. Обновите шаблон src/Blogger/BlogBundle/Resources/views/Blog/show.html
{# src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig #}

{# .. #}

{% block body %}

    {# .. #}

    <section class="comments" id="comments">
        {# .. #}

        <h3>Add Comment</h3>
{{ render(controller('BloggerBlogBundle:Comment:new',{ 'blog_id': blog.id })) }}    
</section>
{% endblock %}


Мы использовали здесь другой тег Twig, render. Этот тег выводит содержимое контроллера в шаблон. В нашем случае мы выводим содержимое BloggerBlogBundle:Comment:new

Если мы посмотрим теперь на одну из страниц блога, такую как http://localhost:8000/2 вы увидите уведомление, показанное ниже.


Это сообщение вызвано шаблоном BloggerBlogBundle:Blog:show.html.twig. Если мы посмотрим на строку 23 шаблона BloggerBlogBundle:Blog:show.html.twig мы увидим, что эта строка показывает, что проблема на самом деле в процессе встраивания BloggerBlogBundle:Comment:create контроллера.

{{ render(controller('BloggerBlogBundle:Comment:new',{ 'blog_id': blog.id })) }}


Если мы посмотрим на сообщение об ошибке внимательнее это даст нам больше информации о причине, почему ошибка была вызвана.
Она говорит нам о том, что поле, которое мы пытаемся вызвать не имеет метода __toString () для сущности, связанной с ним. Поле выбора является элементом формы, которое дает пользователю выбор нескольких вариантов, например, элемент select (выпадающий список). Вы можете быть удивлены, где мы выводим такое поле в форме комментария? Если вы посмотрите на шаблон формы комментария снова, вы заметите, что мы выводим форму с помощью функции Twig {{form_widget(form)}}. Эта функция выводит всю форму. Давайте вернемся к классу формы созданную из класса CommentType. Мы можем видеть, что ряд полей добавляются в форму с помощью объекта FormBuilder. В частности, мы добавляем поле blog.

Если вы помните, во второй части руководства, мы говорили о том, как FormBuilder будет пытаться угадать тип поля для вывода на основе метаданных, относящихся к полю. Так как мы установили связь между сущностями Comment и Blog, FormBuilder предположил, что комментарий должен быть choice полем, которое позволит пользователю указать запись, к которой надо прикрепить комментарий. Вот почему у нас есть choice поле в форме и вот почему была вызвана ошибка Symfony 2. Мы можем решить эту проблему путем реализации __toString() метода в сущности Blog.

// src/Blogger/BlogBundle/Entity/Blog.php
public function __toString()
{
    return $this->getTitle();
}


Подсказка

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


Теперь, когда вы обновите страницу, вы должны увидеть вывод формы комментария. Вы также заметите, что некоторые нежелательные поля были выведены такие как approved, created, updated и blog. Это происходит потому, что мы не настроили сгенерированный класс CommentType ранее.

Подсказка

Все поля, которые были выведены имеют корректный тип. Поле пользователя text, поле комментария textarea, 2 поля DateTime позволяют указать время, и т.д

Это происходит из-за способности FormBuilder угадывать тип поля, которое должно быть выведено. Он способен делать это на основе метаданных, которые вы предоставляете. Так как мы определили вполне конкретные метаданные для сущности Comment, то FormBuilder способен делать точные предположения о типах полей.


Давайте теперь обновим класс, расположенный в src/Blogger/BlogBundle/Form/CommentType.php для вывода только тех полей, которые нам нужны,
вставьте
<?php

namespace Blogger\BlogBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class CommentType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
            $builder->add('user');
            $builder->add('comment');
    }
    
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Blogger\BlogBundle\Entity\Comment'
        ));
    }

    public function getBlockPrefix()
    {
        return 'blogger_blogbundle_commenttype';
    }
}



Теперь, когда вы обновите страницу будут выведены только поле для имени пользователя и поле для комментариев. Если вы отправите форму сейчас, комментарий не будет сохранен в базе данных. Потому, что контроллер формы ничего не делает с сущностью комментария, если форма проходит проверку. Так как же мы сохраним комментарий в базу данных? Вы уже видели, как это делается при создании Фикстур данных. Обновите метод createAction как показано ниже.
// src/Blogger/BlogBundle/Controller/CommentController.php

 public function createAction(Request $request, $blog_id)
    {
        //..

      if ($form->isValid()) {
            $em = $this->getDoctrine()
                ->getManager();
            $em->persist($comment);
            $em->flush();

            return $this->redirect($this->generateUrl('BloggerBlogBundle_blog_show', array(
                    'id' => $comment->getBlog()->getId())) .
                '#comment-' . $comment->getId()
            );
        }
           //..

    }

Сохранение сущности Comment происходит благодаря вызову методов persist() и flush(). Помните, что форма имеет дело с PHP-объектами, а Doctrine 2 управляет и сохраняет эти объекты. Там нет прямой связи между отправкой формы и сохранением представленных данных в базе.

Теперь вы должны иметь возможность добавлять комментарии к сообщениям в блоге.



Вывод



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

Далее мы рассмотрим создание боковой панели (sidebar), чтобы поместить в неё облако тегов и недавние комментарии. Мы также расширим наши знания в Twig и увидим как с помощью него делать пользовательские фильтры. В заключение мы рассмотрим использование Assetic библиотеку, которая поможет нам в управлении нашими assets.

Источники и вспомогательные материалы:

https://symfony.com/
http://tutorial.symblog.co.uk/
http://twig.sensiolabs.org/
http://www.doctrine-project.org/
http://odiszapc.ru/doctrine/

Post Scriptum
Всем спасибо за внимание и замечания сделанные по проекту, если у вас возникли сложности или вопросы, отписывайтесь в комментарии или личные сообщения, добавляйтесь в друзья.


Часть 1 — Конфигурация Symfony2 и шаблонов
Часть 2 — Страница с контактной информацией: валидаторы, формы и электронная почта
Часть 3 — Doctrine 2 и Фикстуры данных
Часть 5 — Twig расширения, Боковая панель(sidebar) и Assetic


Также, если Вам понравилось руководство вы можете поставить звезду репозиторию проекта или подписаться. Спасибо.
Пожалуйста, оцените качество руководства

Проголосовало 42 человека. Воздержался 21 человек.

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

Поделиться с друзьями
-->

Комментарии (113)


  1. ju5tify
    05.06.2016 09:29
    -9

    Вся суть Symfony.
    4 (четыре, ЧЕТЫРЕ) статьи, чтобы сделать один бложик. Поистине, фреймворк для мазохистов.
    Давайте теперь все возьмемся за руки, прочтем несколько мантр про энтерпрайз, бандлы и аннотации и дружно пройдем сертификацию у св. Фабьена.


    1. IncorrecTSW
      05.06.2016 10:40
      +5

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


    1. M-A-XG
      05.06.2016 11:27
      -7

      Говорят, фреймворки ускоряют разработку.
      Но с таким фреймворком, как Симфони приходится долго мудохатся даже с простым блогом.

      Вот трезвый взгляд на фрейморки:
      http://blog.kpitv.net/article/frameworks-1/ (статья обновлена)

      Местная публика явно враждебно настроена против альтернативной точки зрения.


      1. Fesor
        05.06.2016 13:27
        +10

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


        1. Mariik
          05.06.2016 13:54
          +3

          Присоединяюсь к Вашему мнению. Очень здравая точка зрения.


        1. AmdY
          05.06.2016 15:30

          Не знаю, к счастью или к сожалению, но фреймворки лишь маскируют это отличие. Как только автор вышел за пределы фреймворка и начал писать на php, так сразу вылезла substr, которая не дружит с юникодом. Да и truncate для твига есть в экстеншинах, не знаю включён ли он по дефолту в симфони. И я бы счёл это за ошибку в спешке, если бы не пример с php-шаблоном, где автор проигнорировал экранирование выводимых данных, что является смертным грехом и едва ли не главной фичей твига в сравнении с нативными шаблонами.


          1. norgen
            05.06.2016 16:37

            В Symfony еще с версии 2.3 в Twig по умолчанию включено автоматическое экранирование.


          1. Miraage
            05.06.2016 21:44

            А для кого сделаны mb_* функции?


            1. AmdY
              05.06.2016 21:55

              norgen, miraage так я и писал, что не хватает экранирование в примере с нативным шаблоном, и что substr использовать в эпоху юникода нельзя.


        1. M-A-XG
          05.06.2016 16:03
          -3

          1. Если фреймворки так просты, то почему на них выше ЗП?
          2. Фреймворки и так забиты разработчиками, которые без фреймворка не умеют думать.
          3. Адекватные люди — это люди с баблом? Да, у них часто логика типа: вот отвалим дохрена бабла, получим качественный продукт.

          Но часто получаем велосипеды на фреймворках.

          В конторе, в которой я работал с самописной ЦМС, гребут бабло лопатой, так как хорошее портфолио.
          И пофиг, что та ЦМС необновляемая, и пофиг, что с той ЦМС работает только эта студия, и пофиг, что SQL запросы на ней все написаны руками.
          Главное навешать клиенту. :)

          Кстати да, чем больше людей будет на фреймворках, тем более будут ценны настоящие специалисты.

          Я писал и на фреймворках, и на CMS, и на самописи. :)


          1. AmdY
            05.06.2016 22:06
            +3

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

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


            1. DCrystal
              06.06.2016 11:25

              Вот смотрите в статье есть отсылка емейла, без фреймворка
              Справедливости ради, отправкой имейлов занимается не фреймворк, а библиотека.

              письмо слалось не сразу, а сервером очередей и не вызывало дедлок под нагрузкой
              Простите, а как отправка писем может вызывать дедлок?


            1. M-A-XG
              06.06.2016 11:42
              -2

              Я использую внешнюю библиотеку для почты.

              Я ж не против стороннего кода…

              Я против глупостей фреймворков.


              1. Fesor
                06.06.2016 13:31
                +1

                Я против глупостей фреймворков.

                и с такой мотивацией мастерите свои велосипеды? Это как-то нелогично. Ладно бы использовали компоненты и на них строили все.


              1. VolCh
                06.06.2016 13:31

                Что вы считаете глупостями фреймворков? В частности глупостью Symfony?


              1. AmdY
                06.06.2016 15:52
                +3

                Ну так современный фреймворк вроде symfony это и есть набор библиотек. Так что вы занимаетесь самообманом.


          1. aktuba
            05.06.2016 23:33

            >1. Если фреймворки так просты, то почему на них выше ЗП?

            Может потому, что с фреймворками работают люди чуть умнее тех, кто всегда пишет с нуля? Или потому, что эти люди быстрее решают задачи?

            >2. Фреймворки и так забиты разработчиками, которые без фреймворка не умеют думать.

            Ну да… Назовите plz пару известных людей, которые ПРОТИВ фреймворков. Реально интересно…

            >3. Адекватные люди — это люди с баблом? Да, у них часто логика типа: вот отвалим дохрена бабла, получим качественный продукт.

            Нет. «Люди с баблом» — это «люди с баблом». Адекватность и наличие «бабла» никак не пересекаются.

            >Кстати да, чем больше людей будет на фреймворках, тем более будут ценны настоящие специалисты.

            Серьезно?! ))) Расскажите это людям из мира 1С или Java ;)

            >Я писал и на фреймворках, и на CMS, и на самописи. :)

            Не верю! (с) Станиславский. Покажите plz, что вы написали на популярном фреймворке. Судя по вашему посту — вы даже результирующий sql-запрос из AR получить не можете, не то чтобы что-то написать))))


            1. Fesor
              05.06.2016 23:55
              +1

              Может потому, что с фреймворками работают люди чуть умнее тех, кто всегда пишет с нуля? Или потому, что эти люди быстрее решают задачи?

              Это насамом деле очень интересная штука. Допустим разработчик который хорошо знает фреймворк напишет продукт в 3 раза быстрее и за в 2 раза большие деньги, в итоге все в профите. И бизнесу это выгоднее, и продукт раньше выйдет, и разработчик получил больше.


              1. M-A-XG
                06.06.2016 09:29

                Но на фреймворках продукты стоят дороже.


                1. VolCh
                  06.06.2016 09:52
                  +1

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

                  Единственный вариант, по-моему, когда написанный с нуля продукт стоит дешевле — очень низкие требования к качеству кода, включая требования по безопасности, допускающие привлечение очень низкоквалифицированных разработчиков, пользующихся некомпетентностью заказчика в вопросах разработки, когда он может сформулировать, что должно быть в продукте, но не может сформулировать чего быть не должно, а во фреймворках большинство «не должно» из коробки.


                  1. M-A-XG
                    06.06.2016 12:44

                    1. Самопись не значит, что каждый раз все пишут с нуля…
                    2. У меня нету роутинга, маппинга. Проще код — все довольны.
                    3. От атак фреймворк не защищает.
                    4. Во фреймворках нету функционала в коробке, это не ЦМС.


                    1. Fesor
                      06.06.2016 13:33
                      +2

                      1. Самопись значит что введение нового разработчика на проект займет больше времени и сопровождение проекта (устранение уязвимостей, банальный фикс багов) будет обходиться намного дороже.
                      2. Это значит что у вас очень большая связанность системы.
                      3. Он самых распространенных (CSRF, XSS, Sql injection) должен защищать или хотя бы предоставлять возможность минимизировать риски.
                      4. Фреймворки реализуют стандартный функционал связанный с инфраструктурой. В этом их единственное назначение.


                      1. M-A-XG
                        06.06.2016 13:45
                        -1

                        1. Не факт.
                        2. Это Ваша выдумка.
                        3. Никак не защищает. Возможно в шаблонизаторе есть htmlspecialchars по умолчанию.


                        1. Fesor
                          06.06.2016 13:58
                          +2

                          1. Факт. В 99.9% случаев самопись не оправдана ничем кроме как желанием разработчиков поразвлекаться. Это не означает что в ситуациях с фреймворками поддержка всегда будет дешевле — там можно тоже натворить дел, но в среднем стоимость и главное скорость разработки обычно выше, меньше рисков и т.д. У вас же может быть просто не репрезентативная выборка (я полагаю вы работали только с yii причем старым который сейчас смешно рассматривать как достойное решение).


                          2. Простой вопрос. Как вы генерируете ссылки на другие маршруты?


                          3. От CSRF защищает. От XSS защищает нормальный шаблонизатор (twig например). ОТ SQL иньекций защищает нормально настроенная база данных а так же prepared statements которые используют все адекватные люди.


                          1. M-A-XG
                            06.06.2016 16:18

                            2. В конфигах прописаны пути.

                            В Yii 1.1 пути хардкодятся :)


                            1. zelenin
                              06.06.2016 16:51
                              +1

                              что значит «хардкодятся»? в yii1/2 как и в симфони и зенде, ларавеле и фалконе генерация урлов работает идентично — generateUrl($route, $params)


                              1. M-A-XG
                                06.06.2016 17:19

                                А не, работает. Сорри.

                                Это у нас аутсорсеры наговнокодили :)

                                Только контроллер не знает какие пути ему генерировать :)


                                1. Fesor
                                  06.06.2016 18:03
                                  +1

                                  Это у нас аутсорсеры наговнокодили :)

                                  они точно так же на вашем решении наговнокодят, это будет даже в разы проще. Вы же понимаете что проблема в головах а не в инструментах?


                    1. VolCh
                      06.06.2016 13:37
                      +3

                      Самопись не значит, что каждый раз все пишут с нуля…

                      Если структуру приложения не пишут каждый раз с нуля, то значит просто используют самописный фреймворк
                      У меня нету роутинга

                      в корне сайта 100500 *.php файлов, ссылки на которые в шаблоне руками пишутся?
                      маппинга

                      Прямо в базу с браузера стучитесь? Клиент mysql на javascript?
                      От атак фреймворк не защищает.

                      От всех — нет. От распространенных типа csrf или sqlj — да, если не пытаться эту защиту отключать.
                      Во фреймворках нету функционала в коробке, это не ЦМС.

                      Во фреймворках есть функциональность в коробке, нет готовой функциональности по управлению контентом.


                1. aktuba
                  06.06.2016 11:04

                  А есть такое сравнение? Покажите plz.


          1. calg0n
            06.06.2016 09:51

            1. Потому что фреймворки — узкоспециализированная вещь. Найти толкового разработчика с опытом не так просто.
            2. И что в этом плохого? Больше разработчиков, больший выбор для работодателя.
            3. Люди с баблом разные бывают, как тражиры так и скряги.


            1. VolCh
              06.06.2016 09:58
              +1

              1. Полностью кастомные решения куда более узкоспециализрованная вещь, чем решения на сколь-нибудь популярных фреймворках.


              1. calg0n
                06.06.2016 10:05

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


        1. tatenen
          08.06.2016 00:18

          Чем менее компетентен он (м-а-xg), тем больше зп у меня


      1. SiDz
        05.06.2016 13:55

        Жаль автор не показал, своё «самое лучшее» творение, например на гитхабе.

        тот самый код, который, по его словам, может понять каждый разработчик.

        P.s. согласен с Fesor.


        1. Fesor
          05.06.2016 14:23

          К слову да. M-A-XG, выложите на гитхаб как время найдется. Хоть спорить потом можно будет более предметно.


          1. M-A-XG
            05.06.2016 17:07

            http://blog.kpitv.net/article/когда-будет-публичный-релиз-ядра-15373/


            1. Fesor
              05.06.2016 17:21
              +3

              Это пока ноу-хау.

              Так в том же и соль. Сразу указать на недостатки. Один человек не в состоянии продумывать такие вещи.


              Я не хочу распространять код, если он будет труднообновляем.

              От вас требуется лишь пример а не готовый релиз очередного фреймворка


              Я бы еще хотел как-то заработать на этом коде

              не беспокойтесь. Ваш код никому не нужен.


              Перед выпуском в паблик стоило бы подчистить немного легаси код

              так же не беспокойтесь


              Нужно написать документацию.

              и так сойдет


              Я не знаю, как систему встретят, будут ли пользоваться

              напишите в ридми что это только пример "ноу хау" и что бы не пользовались


          1. izac
            05.06.2016 23:15

            А что вы там хотите увидеть если автор сам пишет

            В коде на Битриксе ничего плохого не вижу.
            ?


        1. MetaDone
          05.06.2016 21:09
          +1

          http://govnokod.ru/19878#comment323654
          автор показал его здесь


          1. izac
            05.06.2016 23:13

            да есть и по свежее

            http://blog.kpitv.net/article/how-to-set-title/


      1. LeGront
        05.06.2016 17:07

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


      1. zelenin
        05.06.2016 20:16
        +3

        А, знаете, я прочел статью. Могу сказать, что альтернативной версии там ноль (хотя честно я ожидал такой версии). Но есть очень мало знаний, подмены понятий и обоснования своей т.з. на примере легаси-фреймворка 2008 года.


      1. aktuba
        05.06.2016 23:26

        Ну в вашем посте бреда не меньше, а то и больше… Особенно понравилось вот это:

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

        Т.е. написать фичу на фреймворке — это плохо, написать без фреймворка — это хорошо)


        1. M-A-XG
          09.06.2016 19:14
          -2

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


          1. aktuba
            09.06.2016 19:20
            +1

            Эмм… Например?


            1. M-A-XG
              10.06.2016 19:34

              Я так и не помню все.
              Не записывал. :)

              Нормальные выборки из БД.
              Фреймворковские конструкторы запросов — УГ.

              Ну и ещке, к примеру, мой старший товарищ на прошлой работе зачем-то часть шаблона контроллера вынес в виджет, ибо оно должно выводится выше вызова контроллера.
              А в виджете при этом было типа такого:
              echo SOME_HTML // быдлопарсер не пропускает;

              Говорю, кто эту херню написал?
              Он обидился, говорит, это не херня, просто Yii что-то там не позволяет :)


              1. aktuba
                10.06.2016 20:14

                >Нормальные выборки из БД.

                Абсолютно во всех фреймворках, с которыми я работал, можно использовать plain-sql. Даже если взять в пример не любимый вами yii 1, насколько я помню, можно сделать так: $list= Yii::app()->db->createCommand('select * from post')->queryAll();

                Так, для информации, я сам не использую AR/ORM, но с удовольствием пользуюсь фреймворками.

                >Он обидился, говорит, это не херня, просто Yii что-то там не позволяет :)

                Ну т.е. отваришь ваш мудак, но фреймворки — это плохо)))).


                1. M-A-XG
                  11.06.2016 10:57
                  -3

                  >можно использовать plain-sql

                  1. И привет sql-инъекции. Толковые разработчик заискейпит параметры или использует плейсхолдеры, но часто замечаю, что программисты на фреймворках уверены, что фреймворк каким-то магическим способом защищает от инъекций и допускают ошибки. :)
                  2. Хотелось бы, чтобы sql-запросы писались не вручную, хотелось бы большей гибкости.
                  Вручную замахаешься писать ифы при построении сложного запроса, расставлять пробелы, запятые и т.д.


                  1. Fesor
                    11.06.2016 13:21
                    +1

                    1. почитайте что-ли что такое prepared statements.
                    2. попробуйте doctrine dbal.


                    1. M-A-XG
                      11.06.2016 19:02
                      -1

                      1. Ну так большинство разрабов не применяет подготовленные выражения.
                      2. Вскоре наши сайты переедут на Symfony, посмотрим. :)


                      1. Fesor
                        11.06.2016 19:06
                        +2

                        1. Нет, они просто об этом могут не знать. Но большинство сегодня использует как раз таки prepared statemens за слоями абстракций.
                        2. Не путайте DBAL и ORM. Я боюсь что вы будете одним из большинства кто не правильно готовит доктрину.


              1. MetaDone
                10.06.2016 20:35
                +1

                $select = $query->newSelect();
                $select->from($this->prefix . "meta")
                            ->cols(["*"])
                            ->where('idObject = :idObject')
                            ->join(
                                    'LEFT', 'meta_keys AS s', 'meta.meta_key = s.id'
                                )
                            ->bindValues(array('idObject' => $idObj));
                

                мне кажется все ооочень понятно — что и откуда берем.
                а для случаев сложнее никто не мешает сделать plain-sql и передать туда нужные параметры. Если учесть что в типичном приложении наверно 80% простых запросов — такие штуки увеличивают скорость работы и повышают читаемость.
                Ну а наговнокодить можно в чем угодно, все ограничено только кривизной рук. В самописном движке шансы на получение неструктурированной нечитабельной хрени резко растут.


                1. M-A-XG
                  11.06.2016 12:03
                  -1

                  1. Такой конструктор запроса неудобен, как раз о нем в статье написано.
                  2. Есть потенциальная возможность инъекций.

                  >а для случаев сложнее никто не мешает сделать plain-sql и передать туда нужные параметры

                  Это примерно как жирные модели.
                  Я против такого. Это псевдопрограммирование.

                  Средний уровень говнокода на самописи скорее всего буде выше, так как выше свобода.
                  Но могут существовать конкретно взятые качественные самописи.
                  Фреймворк только маскирует говнокодерство разработчика.


                  1. MetaDone
                    11.06.2016 13:20

                    1. Удобство — понятие субъективное, если не умеете пользоваться — что угодно будет неудобным
                    2. Используйте prepared statement, а не вручную склеивайте запрос как строки

                    Ваша «конкретно взятая качественная самопись» лежит здесь. Если бы вы предложили что-то вменяемое, то диалог имел бы смысл. Конкретно ваша ересь не ускорит разработку проекта, не сделает его более структурированным и легким для входа, поэтому ваше мнение по этому вопросу не следует воспринимать серьезно. Если только вы в таком копаетесь — то пишите что угодно, главное коммерческие проекты не делайте

                    Фреймворк только маскирует говнокодерство разработчика

                    Ваше незамаскировано, в чистом виде — ни структуры, ни стандартов, в контроллере сборщик запросов. Уж не с таким кодом говорить о правильности подходов и архитектуре.


                    1. M-A-XG
                      11.06.2016 18:02
                      -2

                      1. Вы даже не способны заценить красоту игры. :) Мудохаетесь с такими неконструкторами и думаете, что это вершина технической мысли. :)
                      2. Я не о себе говорю, а вообще. Большинство разрабов об этом даже не знает.

                      Приводите ссылку не на коммент, а на сам код, чтобы больше людей могло заценить красоту игры.

                      Любому битриксоиду данный код понятен и он должен заценить.
                      Я на заказ пока и не пишу. Только для себя. А то жалко отдавать ядро за бесцень. :)

                      И это только кусок контроллера.

                      В контроллере не сборщик запросов, а АПИ запрос.
                      Но вы просто задурманены фреймворками и не способны оценить красоту игры.
                      Только прикрываетесь фреймворками, а сами продумать и реализовать архитектуру неспособны.


                      1. Fesor
                        11.06.2016 18:19

                        Вы даже не способны заценить красоту игры


                        вы даже не способы сформулировать четкий список минусов.
                        Я не о себе говорю, а вообще. Большинство разрабов об этом даже не знает.


                        Большинство не знают что используют prepared statements когда работают с query builder-ами. Они дают возможность сэтить параметры к запросам и люди просто этим пользуются. В этом собственно соль, для этого абстракции и вводятся. Что бы меньше думали о всяких там мелочах, о которых кто-то за нас уже подумал.
                        А то жалко отдавать ядро за бесцень. :)


                        Жалко, но придется. Вы впустую тратите свое время.
                        В контроллере не сборщик запросов, а АПИ запрос.


                        Задача контроллера — дернуть один метод приложения (или два, один на запись и один на чтение). Ну и соответственно подготовить данные на вход и результат для вывода. Все. Никаких запросов в базу оно делать не должно.
                        Но вы просто задурманены фреймворками и не способны оценить красоту игры.


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


                        Дайте определение термина "архитектура" в контексте программирования.


                        1. M-A-XG
                          11.06.2016 21:15
                          -2

                          >Большинство не знают что используют prepared statements когда работают с query builder-ами.

                          Я об этом и говорю. Когда они попадают в чистое поле, то творят дыры, ибо сами думать не способны.

                          >Задача контроллера — дернуть один метод приложения

                          Это извращенное понимание MVC. А конкретно — жирные модели.


                          1. VolCh
                            11.06.2016 21:24
                            +1

                            Жирность модели определяется предметной областью приложения (при правильном применении MVC). Есть приложения, где бизнес-логика заключается в тупом CRUD, то есть отсутствует по сути, а есть приложения, где очень сложная инфраструктура и большое множество сложных представлений составляют лишь малую часть от кода бизнес-логики.


                            1. M-A-XG
                              12.06.2016 11:48

                              Согласен.
                              Но в примере на говнокоде — тупой одноразовый CRUD.


                              1. Fesor
                                12.06.2016 15:31

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


                                p.s. для такого тупого CRUD-а есть кодогенераторы вообще.


                                1. M-A-XG
                                  12.06.2016 20:24
                                  -1

                                  Когда станет не тупым, тогда и будем думать.
                                  Вы просто не понимайте красоты игры, поэтому вам кажется, что должно быть разделение.
                                  В данном случае разделения быть не должно. :)


                                  1. MetaDone
                                    12.06.2016 20:51
                                    +2

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


                                    1. Fesor
                                      12.06.2016 20:56
                                      +2

                                      Просто жертва не понимает красоты игры.


                                      p.s. Меня одного настараживают подобные фразы?)


                                      1. MetaDone
                                        12.06.2016 22:15

                                        мне тоже кажется странным, вспоминается «let's play a game» от пилы


                                    1. M-A-XG
                                      12.06.2016 21:39

                                      >маньяк

                                      Оу-оу-оу, палехче.
                                      Тебя, Остап, не в ту степь занесло.


                              1. VolCh
                                12.06.2016 22:27
                                +1

                                Так для тупого CRUD и модель не нужна, максимум DTO.


                      1. MetaDone
                        11.06.2016 18:46

                        если это красиво — то боюсь представить как выглядит корявый код по вашим понятиям. когда излечитесь от синдрома, тогда и продолжим диалог


                  1. Fesor
                    11.06.2016 13:21
                    +1

                    Такой конструктор запроса неудобен, как раз о нем в статье написано.


                    Чем он не удобен?
                    Есть потенциальная возможность инъекций.


                    Нет.


      1. tatenen
        08.06.2016 23:34
        +4

        а мне надоело уже в каментах к каждой статье видеть эти ссылки на блоги и неадекватные доводы.
        сказал а, говори б. покажи уже свое детище — не стыдись.


        1. M-A-XG
          09.06.2016 11:14
          -4

          Мне тоже много чего надоело. :)
          Я критикую фреймворки.
          И я не обещал показать свое детище.
          Бисер перед свиньями метать не собираюсь.
          Может это ты неадекват?


          1. Fesor
            09.06.2016 11:38

            Я критикую фреймворки.


            вы с ними так много работали, я смотрю. Вы критикуете фреймворки на примере только одного, судя по всему с которым пришлось поработать. И вам уже объяснили что Yii это в принципе трэйдоф между качеством и скоростью разработки. Так вы еще и 1-ый Yii смотрите который уже давно устарел.


          1. Fesor
            09.06.2016 11:41
            +1

            Почитал ваш бред про скрам, печально.


            1. M-A-XG
              09.06.2016 16:59
              -1

              Хотите обсудить скрам, пишите у меня на сайте, а не сюда тащите… :)


          1. tatenen
            09.06.2016 11:46
            +2

            в каждом твоем каменте, когда ты пытаешься что-то оспорить то приводишь пример свое детище. а у меня не так а у меня вот так.
            я тоже могу сказать вы все говно — я один дантарьян. но мои слова все адекватные люди пропустят мимо ушей потому что нет как говорится пруфов. я читал что ты не хочешь открывать свое детище, хочешь на нем заработать и тд. но ведь ты можешь для подкрепления своих слов сделать гист конкретного куска кода и показать в качестве аргументов. что тебе мешает?

            Я считаю что фреймворк это склад кирпичей. складывай дом сам.
            Самопись это отливание кирпичей с нуля. дольше, на первый взгляд дешевле обходится кирпич, но в итоге будет дороже (поддержка, порог входа)

            Я не против самописи, но если уже есть готовый компонент — почему бы его не использовать? не нравится пиши свой — трать время.
            Мне как-то техдир запретил использовать pear библиотеки. Типа он напишет свое лучше и тд. в итоге куча дыр, багов, негатива и куча времени на их исправление, пока не взял инициативу в свои руки не начал использовать pear ( было еще до композера).

            Половина недостатков фремворков по твоей версии — это неудобно, непонятно, сложно.
            Все это субъективно.
            Мне допустим все удобно, понятно, и дебажить не сложно — есть xdebug.
            Некоторые пункты вообще смутили:
            — Нету добавления хлебных крошек.
            — У добавленных цсс/жс файлов нету признака, что файл изменился
            — Нету нормальных готовых компонентов, например меню. Меню каждый обязан написать сам

            На мой взгляд это вообще не должно обсужаться. Это больше относится к cms. cms != framework. Да их нет, и быть не должно. Напиши сам. Или посмотри мб кто нить уже написал похожее и выложил на гитхаб/композер.


            1. M-A-XG
              09.06.2016 18:00
              -2

              А зачем примеры кода?
              Вам же даже сам подход не нравится…

              >Я не против самописи, но если уже есть готовый компонент — почему бы его не использовать?
              Допустим я не против фреймворков. Но когда у меня все написано и все сайты на самописи, то смысл в них? :)
              А также у них есть фатальные недостатки. :)
              Если у вас нету своего ядра, то пользуйтесь фреймворками дальше. :)

              >Половина недостатков фремворков по твоей версии — это неудобно, непонятно, сложно.
              Просто есть куда стремиться…

              >Некоторые пункты вообще смутили
              Не знаю, почему смутили. О хлебных крошках и меню я говорю не о шаблонах, а скорее о едином интерфейсе у всех на одном фреймворке.


              1. padlyuck
                09.06.2016 18:27
                +3

                >А зачем примеры кода?
                За тем, что Вы в каждом посте, затрагивающем фреймворки, диким ором орёте о том, что фреймворки гуано, а Вы такой замечательный и у Вас есть своё собственное МЕГАЯДРИЩЕ холеное и лелеяное с 2000какогото года, но Вы нам его не покажете, потому что мы все тут тупорылые снобы, не способные осознать всю красоту и элегантность Ваших решений. А те из присутствующих, кто проникнется Вашими идеями, обязательно бросят свой говёный %framework_name% и побегут пользоваться Вашим божественным продуктом, тем самым лишив Вас чудовищных прибылей.

                Вы постоянно упоминаете какое-то свое ядро абстрактное в вакууме, но пока никто не видил пруфов о том, что оно действительно такое удобное, как Вы его описываете. Соотвественно все упоминания о том что «все фреймворки гавно, у меня свое удобное и правильное ядро» воспринимаются как «я не осилил yii, поэтому написал своё»

                Иметь собственное мнение — это хорошо, но выпячивать его так, как это делаете Вы — абсолютно не обязательно. Тем более что, повторюсь, никто не видел Вашего ядра и о том как всё в нем правильно и замечательно мы можем судить только с Ваших слов.


                1. M-A-XG
                  09.06.2016 20:15

                  Я ж спорю не с кодом фреймворков, а с самим подходом…
                  Зачем тут мой код? :)

                  А мое ядро не публикуется не только потому, что я боюсь потерят мегаприбыли.
                  Там и другие причины есть. А платным оно вряд ли будет. Будут другие способы монетизации.

                  >я не осилил yii, поэтому написал своё

                  Ядро было написано до того, как я сел за Yii.
                  В Украине очень популярный Yii, и на 3 работах доводилось работать именно с дремучим 1.1 :)
                  Он везде был приготовлен некачественно. Но мою критику Yii никто не воспринимал тоже.


                  1. padlyuck
                    09.06.2016 20:54

                    >Я ж спорю не с кодом фреймворков, а с самим подходом…
                    Я так и сказал. Знаете выражение «характер как и член должен быть твердым, и характер так же как и член не стоит выпячивать»? Вот вы именно этим и занимаетесь, выпячиваете свою альтернативную точку зрения. Это было бы хоть сколько-нибудь обоснованным, если бы вы могли привести хотябы куски кода, чтобы не быть голословным, возможно вашу точку зрения и приняли бы как-то. Но вы свои слова о том, что фреймворки говно, а у вас все круто, подтверждаете только своими же словами. Вы не википедия, на слово вам никто не поверит.

                    >В Украине очень популярный Yii, и на 3 работах доводилось работать именно с дремучим 1.1
                    ок, не спорю, yii популярен, но то что вы работали с дремучей её версией — это еще не означает, что все фреймворки говно. Это даже не означает, что yii2 говно. Но вы же наверное даже и не пробовали заглянуть в yii2?

                    Вам не хватает каких-то хлебных крошек, меню и еще какой-то фигни которая не относится к фреймворку. Кто мешает вам написать средствами фреймворка компонент или модуль который реализует нужный вам функционал и тягать его по всем проектам на этом фреймворке? Когда мне чего-то не хватает в юи я просто открываю интересующий компонент юи, полчаса трачу на то, чтобы изучить его код и пишу нужный мне функционал с использованием кода фреймворка(или без использования если нужна универсальность). Дальше этот функционал упаковывается в композер пакет и я могу к любому проекту его прикрутить.

                    >Он везде был приготовлен некачественно.
                    Будьте уверенны наговнокодить можно на чем угодно. Ваша самопись не исключение. Человек не знакомый с деталями вашей реализации с тем же успехом наговнокодит и на вашем ядре. Тут вопрос опять же не к фреймворку, а к прямоте рук которые на этом фреймворке что-то готовят.

                    Опять же, мы почему-то допускаем, что ваше ядро если не идеально, то как минимум написано хорошо, но пруфов никаких и мы вынуждены верить вам на слово. На поверке то может оказаться велосипед на костылях? :)

                    Я собственно говоря к чему растекся мыслью по древу? Обосновывайте свою точку зрения какими-то более авторитетными источниками или примерами своих реализаций. В противном случае — все что вы делаете — просто захламляете темы неподтвержденным мракобесием.


                    1. M-A-XG
                      09.06.2016 22:18
                      -2

                      Смысл в кусках кода, если всех устраивает подход фреймворков?
                      Я не говорю, что у меня все круто.
                      У меня нет описанных недостатков.

                      >это еще не означает, что все фреймворки говно

                      Во всех фреймворках похожие подходы…

                      >Кто мешает вам написать средствами фреймворка компонент или модуль который реализует нужный вам функционал и тягать его по всем проектам на этом фреймворке?

                      Собственно поэтому меня устраивает моя самопись, которую, как вы наконец-то догадались, я не пишу каждый раз с нуля, а тягаю с проекта в проект.

                      >Будьте уверенны наговнокодить можно на чем угодно.

                      Просто фреймворки приподносятся как нечто такое, на котором будет писаться только качественный код, типа приучает к чему-то :)


                      1. padlyuck
                        09.06.2016 23:13

                        >Смысл в кусках кода, если всех устраивает подход фреймворков?
                        >Я не говорю, что у меня все круто.
                        тогда зачем вы так яро пропагандируете свою альтернативно-ориентированную точку зрения? Или вы думаете, что после разговора с вами тот же господин fesor к буям выкинет свой многолетний опыт работы с фреймворками и побежит строгать новый идеальный велосипед? Если уж говорить о своих идеальных велосипедах и конвеере работ, то нормальные люди выбирают фреймворк, на нем пишут свою цмс, к ней пишут кучу всяких модулей которые могут понадобиться, и вот на этой цмс уже клепают сайты. А написать ядро с нуля(пусть и один раз), потом его поддерживать и отлавливать баги и дыры — вы уж меня извините — это дурость и сизифов трудю Как по моему мнению — лучше уж переложить этот труд на разработчиков и комьюнити. Хотя если у вас вагон времени и с десяток сертификатов спеца по компьютерной безопасности — вперед на баррикады

                        >Во всех фреймворках похожие подходы…
                        может быть это чего-то да значит, не задумывались? ;)

                        >как вы наконец-то догадались
                        то что вы её тягаете из проекта в проект все знали изначально, вам скорее указывали на то, что вашу самопись в случае каких-то изменений может быть проблематично обновить на тех проектах которые вы уже реализовали с её помощью(это нужно не всегда, но тем не менее)

                        >Просто фреймворки приподносятся как нечто такое, на котором будет писаться только качественный код
                        Вы свою точку зрения преподносите так же. «Я не использую фреймворки, я крут. А все кто используют их — тупые макаки за компьютерами»


                        1. M-A-XG
                          10.06.2016 09:37
                          -1

                          Я не призываю никого выбрасывать что-то.
                          Читайте выше.

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

                          А не проще взять готовую CMS?

                          >А написать ядро с нуля(пусть и один раз)

                          Это не так и сложно.
                          Мое ядро всего 50 КБ.

                          >может быть это чего-то да значит, не задумывались? ;)

                          Они все пошли по ложной дороге.

                          >вашу самопись в случае каких-то изменений может быть проблематично обновить на тех проектах которые вы уже реализовали с её помощью(это нужно не всегда, но тем не менее)

                          С обновлениями моей самописи проблем нету.
                          На всех сайтах всегда последняя версия, правда обновления не автоматические, а полуавтоматические.
                          С обновлениями самописей вообще могут быть проблемы, как у меня на одной работе, где у каждого клиента было свое ядро.

                          >Вы свою точку зрения преподносите так же.

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


                          1. Fesor
                            10.06.2016 09:59

                            Это только из-за того, как преподносятся фреймворки.


                            А как они преподносятся? Они преподносятся как инструменты для решения проблем. То что люди начинают зацикливаться на них это проблема людей. Это как с языками программирования, есть фанаты и все тут. Рассматривать другие языки они даже не будут, хотя для профессианального развития нужно знать хотя бы парочку очень разных языков с разными подходами.
                            И не столько о самописи, сколько о глупых принципах фреймворков.


                            У вас есть опыт работы только с Yii. А там большая часть "принципов" программирования нарушены в угоду скорости разработки. О каких тогда принципах мы говорим? SOLID дурацкая идея? GRASP?


                            1. M-A-XG
                              10.06.2016 11:01

                              А где я говорил, что SOLID дурацкая идея?


                              1. Fesor
                                10.06.2016 11:13
                                +3

                                ну фреймворки вроде симфони или zend же дурацкие, стало быть и SOLID дурацкая концепция.


                      1. zelenin
                        09.06.2016 23:16

                        >> Во всех фреймворках похожие подходы…

                        https://github.com/zendframework/zend-expressive

                        тут вас что например не устраивает для инфо?


                        1. padlyuck
                          09.06.2016 23:29

                          <sarcasm>а там есть меню и хлебные крошки? без них любой фреймворк гуано и пофигу какая у него история и кто его разрабатывает.</sarcasm>


                          1. zelenin
                            09.06.2016 23:38
                            +1

                            это вы верно говорите, но все же хотелось бы услышать более компетентное мнение )


              1. Fesor
                09.06.2016 18:29

                Если у вас нету своего ядра, то пользуйтесь фреймворками дальше. :)


                ядро означает тот факт, что оно внутри вашего приложения. То есть никакого Inversion of Control. Фреймворки же обычно сверху, снаружи приложения.

                Что будет с вашим приложением через 10 лет?
                Просто есть куда стремиться…


                Вам?
                О хлебных крошках и меню я говорю не о шаблонах, а скорее о едином интерфейсе у всех на одном фреймворке.


                Что если я вам скажу что тенденция выносить графический интерфейс полностью отдельно в виде SPA только продолжит набирать популярность? И в этом случае серверу об этом париться уже не нужно? Например у меня все проекты за последние 2.5 года с SPA или мобильными приложениями, "хлебные крошки" и любая навигация реализуются уже на клиенте.


                1. M-A-XG
                  09.06.2016 21:17
                  -3

                  >ядро означает тот факт, что оно внутри вашего приложения. То есть никакого Inversion of Control.

                  Ядро отдельно, пользовательский код отдельно…

                  >Что будет с вашим приложением через 10 лет?

                  Одному сайту уже 8 лет.

                  >Вам?

                  Создателям фреймворков
                  Но они и их адепты считают, что все норм :)

                  >Что если я вам скажу что тенденция выносить графический интерфейс полностью отдельно в виде SPA только продолжит набирать популярность?

                  Не знаю, что такое SPA.
                  Но я говорил не о графическом интерфейсе, а об АПИ.


    1. tatenen
      08.06.2016 23:32
      +3

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


  1. M-A-XG
    05.06.2016 10:23
    -3

    >Чтобы инвертировать, нам нужно обновить сущность Blog так Doctrine 2 будет знать, что запись может содержать много комментариев. Обновите сущность Blog src/Blogger/BlogBundle/Entity/Blog.php

    Хм, а если нам нужно привязаться к сторонней сущности, то нужно править исходники, которые перезатрутся от обновления?


    1. Fesor
      05.06.2016 13:21
      +2

      А теперь давайте рассуждать… С какой стати сторонняя сущность должна что-то знать о вашей? Она же на то и сторонняя что бы ничего этого не знать.


      Ну и потом, как правило сторонние пакеты предоставляют базовую реализацию сущности, оставляя возможность заменить своей (сущности — это бизнес объекты, а бизнес объекты не должны зависить от внешних зависимостей) или расширять.


      1. M-A-XG
        05.06.2016 15:01

        В моем ядре сущности не знают друг о друге абсолютно ничего, но это не мешает делать между ними связи.

        А на фреймворках такая печальная ситуация, что связи со сторонними сущности не сделаешь :)

        Вообще наличие этих прописанных связей плохо пахнет.

        Комментарии ничего не должны знать о постах и наоборот.

        >а бизнес объекты не должны зависеть от внешних зависимостей

        Я не понимаю этой фразы… :)

        У фреймворков есть маркетплейс, например, как в Битриксе?

        >оставляя возможность заменить своей

        Написать с нуля? Или отнаследоваться?

        С нуля писать наверно плохо. Это потом придется поддерживать актуальность. Это дублирование кода. И есть заповедь: Не плоди сущностей. :)

        Наследование? Где будем сохранять ее? В своем бандле? Аннотации (комментарии) не наследуются.


        1. Fesor
          05.06.2016 16:42
          +1

          В моем ядре сущности не знают друг о друге абсолютно ничего, но это не мешает делать между ними связи.

          В "ядре" не должно быть сущностей. Ну и если у вас есть связи, то они должны знать друг о друге либо сверзу будет объект хранящий свящи и управляющий этим всем.


          А на фреймворках такая печальная ситуация, что связи со сторонними сущности не сделаешь :)

          Вы можете указать связь вашей сущности со сторонней, но сторонняя не должна ничего знать о вашей сущности. И с этим в "фреймворках" проблем нет. Ну и опять же — мы сейчас говорим даже не о фреймворках а о ORM, которые нужны даже не в 90% случаев.


          Вообще наличие этих прописанных связей плохо пахнет.

          Давайте отстранимся от фреймворков и PHP на секундочку и вспомним о такой забытой штуке как UML. И представим что мы не проектируем "архитектуру" а просто сидим с клиентом и он объясняет как у него сейчас построен процесс или как он хочет что бы оно работало. Он вам рассказывает:


          Ну… у меня есть товары, которые хранятся на складе. Складов у нас несколько, и нужно знать какой товар на каком складе. Причем товары могут перемещать со склада на склад (логистические загоны)… Этим занимаются управляющие складом.


          То есть мы видим что склад должен знать о том, какие продукты в нем хранятся. И следовательно у сущности склада будет свойство products в котором будет храниться коллекция всех продуктов на этом складе. Логично? Так мы можем прямо в сущности реализовать метод для проверки есть ли на складе столько продуктов, можно организовать резервирование оных и т.д.


          Я не понимаю этой фразы… :)

          Есть такой подход к проектированию, который собственно исповедует Doctrine. Наши сущности — это просто PHP объекты, которые ничерта не знают ни о доктрине, ни о симфони… Они просто хранят какое-то состояние и поведение. Объекты предметной области. Например объект склада умеет хранить продукты, умеет их резервировать и все такое. Но трансфер между складами уже за рамками обязанностей одного склада и подобную логику нужно выносить на уровень выше.


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


          К сожалению подавляющее большинство разработчиков выносит поведение из сущностей в менеджеры, и сущности превращатюся в тупые хранилища данных. Тем самым мы снижаем зацепление наших объектов, ломаем принцип единой ответственности и усложняем взаимодействия, как минимум нарушаем инкапсуляцию частенько. Просто так, на ровном месте. Но это проблема не фреймворков а людей.


          Написать с нуля? Или отнаследоваться?

          it depends. Например взять FosUserBundle. Для простеньких проектов можно отнаследоваться, а для проектов посложнее можно написать полностью свою реализацию (когда переопределение выходит не так гибко). Сам же FosUserBundle ожидает любую сущность имплементирующую AdvancedUserInterface так что никаких проблем с поддержкой.


          Аннотации (комментарии) не наследуются.

          наследуются свойства. А стало быть если вы не переопределяли свойства — то наследуются и аннотации.


          1. M-A-XG
            05.06.2016 18:14

            >В «ядре» не должно быть сущностей.

            Они и не в ядре. Просто ядру не нужно описание связей.

            >ORM, которые нужны даже не в 90% случаев

            Может не нужны в 90% случаев. У меня нету ОРМ.

            >То есть мы видим что склад должен знать о том, какие продукты в нем хранятся.

            Если не использовать ОРМ, то никто ничего не должен знать :)

            >К сожалению подавляющее большинство разработчиков выносит поведение из сущностей в менеджеры, и сущности превращатюся в тупые хранилища данных.

            В мире веб у сущностей и не должно быть поведения. Есть только выбор элементов, но и его пихать в сущность не стоит. Я за тупые хранилища данных.

            И многие разработчики держат выбор элементов в сущности (Yii это даже считает лучшей практикой :) Не знаю, как другие), ну или создают одноразово используемые тупорылые методы вроде get10LastNews() например в контроллере.

            >А стало быть если вы не переопределяли свойства — то наследуются и аннотации.

            А как мы добавим связь, не переопределив свойство?


            1. Fesor
              05.06.2016 18:37
              +1

              В мире веб у сущностей и не должно быть поведения.

              Нет никакого мира WEB. Мы сейчас говорим про серверную часть в контексте клиент-серверной архитектуры. Это одно приложение. Оно может даже ничего не знать о WEB (представьте что у вас просто CRON-ом дергаются скрипты). Для приложения нет разницы запускаются ли команды через cron, выполняются из очередей или же по HTTP запросу. Как только мы перестаем думать мысли в духе "это web тут наверное по другому", все становится намного проще.


              Ну и собственно когда у сущностей нет поведения, это называется анемичной моделью и признано антипаттерном. Почему антипаттерном? Потому что вам все еще нужно описывать эту модель но она никакой пользы вам не дает. Тот код который можно было писать прямо внутри сущности мы выносим в какие-то другие вещи, и при этом дергаем кучи сеттеров.


              Как итог — читая код вы не сможете быстро понять какие бизнес-ограничения налагаются на ваши сущности. То есть вам придется в разы больше времени потратить что бы разобраться в предметной области по коду (например когда вы вводите нового разработчика в проект или просто работаете с проектом который писали год назад).


              Есть только выбор элементов, но и его пихать в сущность не стоит. Я за тупые хранилища данных.

              А что такое элементы в контексте вопроса? Я правильно понимаю что вы про такие штуки (в контексте Yii):


              Something::findStuff(); // так?

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


              Так же между AR в Yii (и другие реализации) и Doctrine есть одна огромная разница:


              Doctrine оперирует объектами, и мэпит их на базу данных. То есть один объект можно замэпить хоть на 10 таблиц или 10 объектов замэпить на одну таблицу. Тогда как в случае с AR наши "модельки" это прямая проэкция рядов таблиц.


              В случае AR ответственность за управление рядом таблицы лежит на самой моделе. В случае Doctrine "модельки" ничерта не знают о базе данных и вводится объект-репозиторий, который и является "тупым хранилищем". В нем максимум можно расположить логику, ограничивающую можно ли ложить объект в репозиторий или нет.


              ну или создают одноразово используемые тупорылые методы вроде get10LastNews() например в контроллере.

              Ну печально. У меня в репозиториях будет метод getLatestNews($limit = 10)


              А как мы добавим связь, не переопределив свойство?

              Повторюсь еще раз. Если мы наследуемся от сторонней сущности, там нет связей с нашими сущностями потому что… ну сторонний пакет понятия не имеет о том кто и как будет его использовать.


              А если связи нет и хотим ее добавить, то мы добавляем свойство а не "переопределяем".


              1. M-A-XG
                05.06.2016 19:17

                >признано антипаттерном

                Божественный объект тож признан антипаттерном, но jQuery это не мешает :)

                >Потому что вам все еще нужно описывать эту модель но она никакой пользы вам не дает.

                А я у себя ее и не описываю. 3 строчки кода для наследования базового класса, где указана таблица, с которой работать и все :)

                Я не разделяю жирных моделей. Все должно быть просто. Какое поведение в вебе? Выбрать из базы и записать в базу. Неоходимо грамотное АПИ для этого, а не тонны лишнего кода.

                Или письма отправлять из сущностей? :)

                >А что такое элементы в контексте вопроса?

                Записи в БД (результаты запросов).

                Я против методов get10LastNews и get10FirstComments (ну или без 10, например, getLastNewsByAuthor)

                >Если мы наследуемся от сторонней сущности, там нет связей с нашими сущностями потому что…

                У стороннего пакета могут быть связи сущности с его сущностями.


                1. IncorrecTSW
                  05.06.2016 19:27
                  +2

                  jQuery совсем из другой области. Да и по большей части это неймспейс уже.


                1. Fesor
                  05.06.2016 20:02

                  Божественный объект тож признан антипаттерном, но jQuery это не мешает :)

                  а где там god object? Там все весьма грамотно.


                  3 строчки кода для наследования базового класса, где указана таблица, с которой работать и все :)

                  И тогда мы говорим не о "моделях", "сущностях" и т.д. а о старом добром row data gateway.


                  Я не разделяю жирных моделей. Все должно быть просто.

                  Все должно быть в меру. Тощие модели тоже никто не любит. Они должны быть сочными но не жирными. Что бы было за что ухватиться, но что бы проходили в дверь.


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


                  Или письма отправлять из сущностей? :)

                  Это другая крайность. Чисто теоритически через дабл-диспатч и свои интерфейсы (то есть мы передаем сервис реализующий интерфейс, лежащий рядом с сущностью, в качестве аргумента какого-то метода этой сущности) в этом даже не будет ничего плохого. Но мне больше нравится комбинация domain events + event dispatcher для подобного либо просто явно задавать эти действия в сервисах уровня приложения.


                  Записи в БД (результаты запросов).

                  конкретно вы говорили о чтении из БД. Запись в БД в контексте active record это как раз таки дело самой модели. Если вам не нравится и нужен именно ORM — то доктрина (или любой датамэппер) хороший выбор. А если не нужен ORM — лучше использовать table gateway.


                  Я против методов get10LastNews и get10FirstComments (ну или без 10, например, getLastNewsByAuthor)

                  размажем SQL повсюду? Где-то же это должно быть изолировано? А стало быть и методы такие появятся. Вопрос только в том, где они будут расположены и насколько нам будет удобно это тестировать и использовать.


                  У стороннего пакета могут быть связи сущности с его сущностями.

                  Могут быть конечно. Но зачем вам их переопределять? В любом случае доктрина предоставляет возможность переопределять мэппинги так гибко.


                  1. M-A-XG
                    05.06.2016 21:48

                    >конкретно вы говорили о чтении из БД

                    «Записи» — не процесс записи, а строчки в таблице. :)

                    >row data gateway, table gateway

                    Тут описание какое-то унылое: :)
                    http://www.design-pattern.ru/patterns/table-data-gateway.html
                    http://www.design-pattern.ru/patterns/row-data-gateway.html

                    У меня же нормальное АПИ для работы с БД, ну и пофиг, что для него нету умного термина :)
                    Ну или термин есть, но у них реализации унылые, а у меня норм :)

                    >размажем SQL повсюду?

                    Изолировано должно быть, но без таких методов :)

                    >Но зачем вам их переопределять?

                    Не переопределять, а добавить. Связывание не по первичному ключу, а по двум вторичным. Если предоставляет, то ок :)


                    1. Fesor
                      05.06.2016 22:41
                      +2

                      У меня же нормальное АПИ для работы с БД, ну и пофиг, что для него нету умного термина :)

                      Покажите. Ибо я могу судить только по этому примеру: http://govnokod.ru/19878#comment323654


                      И называется это процедурное программирование без явного разделения ответственности. Просто говнокод. Вполне себе академический термин :)


                      В целом же подозреваю что вы организуете DAO или другими словами Table Gateway + кастыли.


                      1. M-A-XG
                        06.06.2016 10:35

                        АПИ примерно такое, как у инфоблоков Битрикса.


                        1. Fesor
                          06.06.2016 10:50

                          не работал с битриксом и не хочу.


                          1. VolCh
                            06.06.2016 11:34
                            +4

                            Работал с Битриксом — больше не хочу.


          1. VolCh
            06.06.2016 07:33

            К сожалению подавляющее большинство разработчиков выносит поведение из сущностей в менеджеры, и сущности превращатюся в тупые хранилища данных.


            Отчасти это связано с некоторыми особенностями Doctrine. Например, в сущность сложно заинжектить сервис, особенно лениво.


            1. Fesor
              06.06.2016 10:02

              в сущности не стоит инджектить сервисы, как по мне. А передать сервис через дабл диспатч — без проблем.


  1. M-A-XG
    05.06.2016 13:53

    Не та ветка


  1. VolCh
    06.06.2016 08:32
    +3

    Не вижу смысла для данной задачи ни создавать CommentRepository, ни получать их отдельно в контроллере, ни передавать их отдельно в шаблон. По данной задаче комментарии не являются самостоятельными сущностями, они лишь агрегат в блоге. Наивная (и академически чистая :) реализация могла бы сводиться к методу getApprovedComments в Blog:

    public function getApprovedComments() 
    {
      return $this->comments->filter(function (Comment $comment) {return $comment->getApproved()})
    }
    
    , который вызывался бы непосредственно в шаблоне, а потом, при оптимизации создать метод в репозитории блога (как корня агрегата), который получал бы из базы блоги только с утвержденными комментариями.

    Кстати о комментариях. Имена типа $comment->getComment() обычно неудобны и сбивают с толку и в коде, и в базе (comment.comment). Лучше использовать text, body, content, в конце-концов просто value. А булевы поля для статуса записей быстро себя исчёрпывают, в частности уже сейчас если поле approved === FALSE, то нельзя однозначно сказать, то ли уже одобренная запись была разодобрена, то ли это новая, которую ещё не просматривали, то ли просмотрели и отклонили.

    Да, даже если остановиться на булевом поле, то по соглашениям для таких полей имя isApproved, а не approved, что гораздо понятнее. Кстати, тогда Doctrine и метод сгенерирует не getIsApproved, а isApproved, что удобнее. И если потом перейти к каким-то другим статусам, то можно просто переписать код иззера isApproved() не изменяя клиентского кода. Кстати, ещё по замечание по именованию — принято, что тайстамп поля именуются в мире доктрин/симфони как createdAt и updatedAt, более чётко определяя сущность поля.


    1. Fesor
      06.06.2016 10:03
      +1

      маленькая поправка. Можно сделать extra-lazy связь и вместо filter использовать matching с критерией. Тогда сформируется правильный SQL запрос и не придется грузить всю коллекцию. Хотя это мелочи.


      1. tatenen
        08.06.2016 23:54

        можете показать пример как надо правильно?


        1. SiDz
          09.06.2016 08:41
          +2

          http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html — про ExtraLazy

          http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/working-with-associations.html#filtering-collections — про matching.

          Очень удобная и крутая штука. Рекомендую.


          1. tatenen
            11.06.2016 00:39

            спасибо что ответили. обязательно ознакомлюсь