Pull to refresh
2
0
Send message
Я говорил не про использование YAML как такового, а про то, что чтобы задать нужную логику, надо писать специальные магические константы, которые где-то там в движке обрабатываются и типа "оно само" работает. А если в одном разделе надо разметку "чуть-чуть поменять", то начинаются танцы с бубном.

Это надуманные проблемы, и Yii/AR не спасет вас от этого.
У вас config.php из кастомного DSL.
Вы же настраиваете, как минимум роуты, и они по вашей логике тоже магия.
Eсли бы у вас было немного опыта на symfony, вы бы поняли как это работает, легко нашли бы место где это обрабатывается.


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

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


А REST API это типа не CRUD)

Очевидно АПИ это не только круд, да и есть ещё много чего, где то понадобиться WS, поиск, интеграция с внешними АПИ, или что то ещё в зависимости от предметной области проекта.


Поддержка REST API в Yii встроенная (в новых версиях вроде вынесли отдельно), занимает несколько файлов, и думаю простота использования AR здесь сыграла не последнюю роль.

Разве что AR легче было реализовать, т.к. DM требует больше абстракции в реализации, но при использовании никакой разницы в скорости нет.


Попробовал. Вписывать ответы вручную гораздо дольше, чем проектировать схему БД через GUI-клиент. Вот из таких мелочей и получается быстрее.

Это немного субъективно, на самом деле необязательно генерировать из консоли, просто вы так любите генерировать, так я и показал что так тоже можно.
Проще самому создавать модели, и там же всё настраивать через аннотации, поддержка IDE прекрасная, а скорость приходить с опытом.
Поймите, если я буду создавать AR модели, то для меня это больше когнитивная нагрузка, просто потому что я не работаю с этой штукой каждый день.
К тому же, никто вам не запрещает проектировать в каком нибудь GUI (datagrip/mysql workbench), и генерировать всё остальное одной командой.


Ни сортировки, ни страниц, надо руками вписывать. Ах да, библиотеки. Но тогда ваш пример это не "нечто подобное".

Ваши претензии к тому что datagrid не идёт в комплекте с фреймворком?




В итоге, получается удобство AR для прототипов, это наличие datagrid в Yii из коробки?

  • Можно написать маленькую часть системы как зависимость, и в последующих версиях переопределить assert'ов.
  • Более надежный способ, написать библиотеку для генерации юнит тестов.

symfony тоже позволяет нечто подобное:


curl -sS https://get.symfony.com/cli/installer | bash
symfony new --full my_project; cd my_project
bin/console make:user
bin/console make:registration-form
bin/console make:auth
bin/console make:entity
bin/console make:crud
bin/console doctrine:database:create
bin/console make:migration
bin/console doctrine:migrations:migrate --no-interaction
# ...

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


Так а на что у вас остальное время уходит тогда? Построение БД занимает одинаковое время и там и там. Генерация моделей со связями ну пусть тоже одной командой.

Прототип не только о крудах, это может быть апи(REST/GraphQL) на apiplatform, и фронтенд SPA/мобильное приложение, вот на них и уходить остальное время.


То есть объем изменяемого вручную кода сопоставим с размером конфига Knp Rad Resource Resolver.

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


Knp Rad Resource Resolver. Который к тому же не PHP код, а отдельная магия. В Symfony количество файлов и методов будет побольше.

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


+ этот конфиг стандартный для symfony/dependency-injection, т.е. его можно писать хот на php, а информацию о конфиге легко получить стандартными средствами: bin/console config:dump-reference <bundleName>

Интересно, какие особенности doctrine мешали вам разрабатывать с той же скорости?


Не могло влиять, перевес опыта в одну сторону?

Пока только конфиг напишешь, больше времени уйдет.

Конфиг можно генерировать из схемы, и потом модели из конфига, doctrine-cli это умеет из коробки.


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


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


Если у вас есть время, можем проверить на каком-нибудь примере. С коммитами на каждый шаг, чтобы можно было проверить объем кода.

К сожалению у меня нет ни времени, ни желания на это.
К тому же, возможно, у нас с вами разная скорость разработки, не зависимо от инструментов.




Понятно дело, сужу исходя своего опыта, на AR правда приходиться мало, примерно год.

Создать схему в БД через GUI клиент, проставить внешние ключи, генератором сгенерировать модели со всеми связями

Doctrine умеет тоже самое.


вывести в грид с заменой user_id на user.name, с автоматической пагинацией и произвольной сортировкой, и возможностью сложных фильтров и обработкой N+1 (везде названия связей, а не таблиц)

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


В общем, для прототипов symofny/doctrine подходить не хуже фреймворков с AR, особенно с появлением symfony/flex.
Вот тут можно глянуть на компоненты для RAD разработки http://rad.knplabs.com/




P/s не спорю с тем что Laravel/Yii/etc подходить для "чего угодно", просто хотел прояснить, что в symfony/doctrine можно тоже самое сделать, за то же время.

И? В моем комментарии про doctrine нет ни слова. Он о том, где лучше использовать elloquent, где его не стоит использовать и где я его чаще вижу.

Именно про doctrine ни слова, но:


Быстро накидать домашний проектик — на это реально уходит меньше времени.

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


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

Результат вашего теста может на прямую зависеть от других тестов

По своей природы, юнит тесты атомарные, и не зависят от ничего, для них оправдано держать отдельный конфигурационный файл.
А то что тесты не проходят, когда кто то пытается использовать реальную БД в юнит тестах — прекрасно, так даже лучше.
Вам довелось видеть среди юнит тестов, запрос к внешней АПИ, которому место в интеграционных тестах?


Это очень плохая практика. Поднятие всего окружения для каждого теста (когда их много) — очень ресурсоёмкая задача.

Поднять всего окружения не обязательно.
Достаточно один раз поднять БД из фикстур, сделать стаб для DB, и обернуть тестов в rollback-only транзакцию.
Это как раз становиться актуальным, когда у вас очень много тестов.

ИМХО, не стоит чрезмерно упрощать ЯП, лучше когда IDE будут поддерживать и развивать LSP, а ЯП пуст сам реализует LSP сервер.
Быстро свести вмести модельки и посмотреть, как они взаимодействуют.

На doctrine это проще делается, вы можете проектировать в любой IDE для БД, или в коде.
Приведите пожалуйста пример, какую структуру удобно было бы проектировать на Eloquent.

Я так понимаю, вы о функциональных тестах(а где ещё может понадобиться реальный класс DB?).

Нужно лишь сделать отдельную конфигурацию для юнит тестов, либо можете запустить тесты параллельно.
Вы правы, была ошибка в логике теста, но тут уж не важно используем мы AR или DM.
Обновленный тест
<?php declare(strict_types=1);

namespace App\Tests;

use Psr\Log\LoggerInterface;
use Mockery as m;
use App\MyModel;
use App\DB;
use App\SomeService;
use App\Request;
use PHPUnit\Framework\TestCase;

class SomeServiceTest extends TestCase
{
    private $logger;
    private $someService;

    protected function setUp(): void
    {
        m::globalHelpers();
        $this->logger = mock(LoggerInterface::class);
        $this->someService = new SomeService($this->logger);
    }

    /**
     * @param bool $commitShouldFail
     * @dataProvider storeDataProvider
     */
    public function testStore(bool $commitShouldFail): void
    {
        $db = mock(\sprintf('alias:%s', DB::class));
        $db->shouldReceive('beginTransaction')->ordered(1)->once();
        $request = new Request();
        $requestName = 'ada1b238-2703-4064-b2ed-7f6ef2f4a44b';
        $request->name = $requestName;
        $myModel = mock(\sprintf('overload:%s', MyModel::class));
        $myModel->shouldReceive('__set')
                ->with('name', $requestName)
                ->andSet('name', $requestName)
                ->ordered(2)
                ->once();

        $myModel->shouldReceive('save')->ordered(3)->once();

        if ($commitShouldFail) {
            $exceptionMessage = 'b687f5d7-6347-4b35-8fa7-ec8bbe73fdd8';
            $db->shouldReceive('commit')
               ->ordered(4)
               ->once()
               ->andThrow(\Exception::class, $exceptionMessage);
            $db->shouldReceive('rollback')->ordered(5)->once();
            $this->logger
                ->shouldReceive('error')
                ->with($exceptionMessage, m::any())
                ->ordered(6)
                ->once();
        } else {
            $db->shouldReceive('commit')->ordered(4)->once();
        }

        $this->someService->store($request);
    }

    public function storeDataProvider(): array
    {
        return [
            [true],
            [false],
        ];
    }

    protected function tearDown(): void
    {
        $this->addToAssertionCount(
            m::getContainer()->mockery_getExpectationCount()
        );

        m::resetContainer();
    }
}

Вы недооцениваете хороших тест фреймворков
<?php declare(strict_types=1);

namespace App\Tests;

use Psr\Log\LoggerInterface;
use Mockery as m;
use App\MyModel;
use App\DB;
use App\SomeService;
use App\Request;
use PHPUnit\Framework\TestCase;

class SomeServiceTest extends TestCase
{
    private $logger;
    private $someService;

    protected function setUp(): void
    {
        m::globalHelpers();
        $this->logger = mock(LoggerInterface::class);
        $this->someService = new SomeService($this->logger);
    }

    /**
     * @param bool $commitShouldFail
     * @dataProvider storeDataProvider
     */
    public function testStore(bool $commitShouldFail): void
    {
        $db = mock(\sprintf('alias:%s', DB::class));
        $db->shouldReceive('beginTransaction')->once();
        $request = new Request();
        $requestName = 'ada1b238-2703-4064-b2ed-7f6ef2f4a44b';
        $request->name = $requestName;
        $myModel = mock(\sprintf('overload:%s', MyModel::class));
        $myModel->shouldReceive('__set')
                ->with('name', $requestName)
                ->andSet('name', $requestName)
                ->once();

        $myModel->shouldReceive('save')->once();

        if ($commitShouldFail) {
            $exceptionMessage = 'b687f5d7-6347-4b35-8fa7-ec8bbe73fdd8';
            $db->shouldReceive('commit')
               ->once()
               ->andThrow(\Exception::class, $exceptionMessage);
            $db->shouldReceive('rollback')->once();
            $this->logger
                ->shouldReceive('error')
                ->with($exceptionMessage, m::any())
                ->once();
        } else {
            $db->shouldReceive('commit')->once();
        }

        $this->someService->store($request);
    }

    public function storeDataProvider(): array
    {
        return [
            [true],
            [false],
        ];
    }

    protected function tearDown(): void
    {
        $this->addToAssertionCount(
            m::getContainer()->mockery_getExpectationCount()
        );

        m::resetContainer();
    }
}


Information

Rating
Does not participate
Registered
Activity