Компонент от ауры я могу одной строкой подключить в свой проект, Ваш — не знаю как долго и имеет ли смысл выдирать его
Здесь в зависимость модуля одна строчка в meta.json (пример), и ещё одна если ещё нет зависимости от Composer (пример).
The query objects do not execute queries against a database. When you are done building the query, you will need to pass it to a database connection of your choice.
Не вижу никаких проблем кроме того, что именованные подготовленные выражения не поддерживаются.
Пример из документации:
// a PDO connection
$pdo = new PDO(...);
// prepare the statment
$sth = $pdo->prepare($select->getStatement());
// bind the values and execute
$sth->execute($select->getBindValues());
// get the results back as an associative array
$result = $sth->fetch(PDO::FETCH_ASSOC);
Я просто выставлю оба примера рядом чтобы было наглядно видно что короче, а что нет. Хотя я бы сказал, что оно соразмерно практически 1 в 1, поскольку, повторюсь, в подобных ситуациях query builder прямо дублирует SQL синтаксис.
$red = Fruit::select('name', 'colour', 'calories')->where('calories', '<', 150)->where('colour', 'red')->get();
$red = $dbh->qfa('SELECT name, colour, calories FROM fruit WHERE calories < ? AND colour = ?', 150, 'red');
Если вы пойдете дальше и будете использовать ActiveRecord с описанием моделей, в том числе phpdoc «property type $column» у вас будет просто замечательный автокомплит.
Согласен, но мы же говорим пока (имеется ввиду содержимое поста) о произвольных запросах. ActiveRecord слегка уровнем абстракции выше. Его вы, в конце концов, и здесь можете подключить при желании.
Как минимум, QueryBuilder в паре с ActiveRecord (laravel5, yii2) позволяют куда удобней работать с БД чем писать запросы вручную.
Для этого есть cs\CRUD и cs\CRUD_helpers, которые позволяют вообще обойтись и без SQL и без конструирования запросов. Всё сводится к декларативному описанию структуры таблиц и вызовом одного метода.
Пример в системном классе: структура, чтение, обновление данных.
Кроме того, ООП-синтаксис запросов (builder/ar) куда приятней plain-text'a в sql, а при адекватно настроенной IDE позволяет существенно упростить их написание (при помощи autocomplete). Посмотрите так же на длину и лаконичность запроса через builder/ar либо классический sql.
В том то и дело, что в примерах Query builder не короче, а из автодополнения вы получите только методы. В то время как в SQL вы получите красивую подсветку синтаксиса, автодополнение SQL команд, в том числе тех, о которых Query builder не знает, а так же полей из реальных таблиц. Query builder ну никак здесь не выигрывает у SQL, даже близко. Это не учитывая того, что SQL может читать любой, а в синтаксисе конкретного Query builder-а и его ограничениях нужно ещё разбираться.
В querybuilder'e вы не увидете таких извращений, а запросы select->fetch далаются в 1 строку и вполне читаемы.
А здесь, простите, сколько строчек? Чем вам не читаем любезно раскрашенный IDE SQL?
Такой конструкцией вы усложняете разработку под вашу cms другим людям, ведь это вовсе не классический вид pdo_prepared запросов и не querybuilder (стороннему разработчику придется тратить время и нервы чтобы приноровиться к такому стилю).
Дело в том, что абстрагирования там всего ничего, большинство запросов на 100% повторяют синтаксис SQL запросов. Как по мне, так проще сделать несколько простеньких конвертаций как в примерах, и дальше писать SQL.
На самом деле бывает больше двух запросов, тогда и больше строчек моэно сэкономить, и читать визуально проще. Пример 3х запросов.
Под капотом всё очень прямолинейно, но потенциально есть возможность объединять запросы и делать из нескольких запросов один запрос в БД, просто с этим есть некоторые сложности (mysqli_multi_query() ведет себя не совсем так, как хотелось бы, PostgreSQL не желает делать множественные запросы с серверными подготовленными выражениями, SQLite, по-моему, вообще множественные запросы не поддерживает).
Сейчас активно используются emoji, и нужно быть готовым к тому, что такие символы попадутся в произвольном тексте.
Так что я бы предположил, что даже англоязычные разработчики его используют довольно часто.
Полагаю, имеется ввиду что PHP перекомпилировать для подключения модулей совсем не нужно, их можно подключать и отключать по необходимости.
Более того, mb_string достаточно популярный модуль в любом случае.
Есть события, с их помощью вы можете делать любые алиасы, которые вам только заблагорассудится.
Если не указывать язык в URL — будут взяты к сведению заголовки запроса. К примеру, Accept-Language если это браузер, так же поддерживаются заголовки, которые отправляет Facebook, когда встраивает preview в ленте (ему почему-то не хотелось стандартный Accept-Language применять).
Далее если и заголовков нет никаких — используется язык, сконфигурированный в настройках системы.
Так же если пользователь зарегистрирован и в профиле явно указан язык — он так же будет иметь бОльший вес, чем язык в заголовках и чем в настройках системы.
Но это уже не совсем к маршрутизации относится, это многоязычность, с которой тоже много все интересного происходит)
Моки и стабы на используемый внешний код и вперёд. В инструментах для разработки самого фреймворка такие штуки уже есть готовые и активно используются, при желании можно любые собственные инструменты притащить.
Естественно, я имею ввиду что оно полностью ложится на конвенцию. Если понять несложный принцип, то всё сразу становится на свои места, не нужно изучать код и конфигурацию для того чтобы с почти 100% вероятностью узнать что будет происходить и где это искать.
Во фреймворке нет очевидной связи между определённым маршрутом и моделью, к примеру, статьи. Соответственно, нет простого способа в общем виде ассоциировать какие-то сущности с маршрутами и обратно. В последнее время всё больше перехожу на подход API + по сути отдельное приложение на фронтенде, которое работает с API. В этом случае пути всё равно придется генерировать не на сервере. В общем, нужно будет над этим подумать.
Не вижу какой-то фундаментальной разницы. В то же время, в Symfony не так очевидно, какой код отвечает за команду, а в CleverStyle Framework я не смотря в код знаю, что за запрос отвечает статический метод cs\modules\System\cli\Controller::optimization_clean_cache().
Почему именно так? Мне это показалось весьма логичным и удобным (как в HTTP запросах: метод и цель), а в контексте остальной маршрутизации даже в некотором смысле очевидно. Вы выполняете операцию (clean_cache) над сущностью или в контексте определённого пути (System/optimization).
Если бы мы создавали статьи из командной строки, то у нас были бы методы get, post, delete и сущность Articles. То есть в формате Symfony получается articles:get, а здесь get:Articles, хотя суть та же.
Они и есть консольные команды, просто формат для унификации аналогичен остальной маршрутизации.
CLI команд сейчас всего несколько, к примеру, очистить кэш можно следующим образом:
./cli clean_cache:System/optimization
Где clean_cache выступает в роли аналога HTTP метода, а System/optimization в роли аналога пути страницы.
Параметры командной строки превращаются в унифицированные параметры, доступные через привычный $Request->query(), аналогично параметрам после ? в URL:
./cli help:System выведет справку с доступными командами и форматом вызова (аналогично вызову ./cli без каких либо параметров).
Как в вашей системе получить ссылку на комментарий к созданному мной элементу каталога?
Увы, фреймворк ничего такого из коробки не предоставляет. Не знаю почему, но мне как-то даже не было нужно такое никогда. Можете уточнить для чего конкретно такое может быть нужно, что нельзя вручную сформировать ссылку? Интересно узнать сценарии использования.
1) что такое "ручное внедрение зависимостей"? Фабрики? Что там нечитабельного?
Я имел ввиду подобное:
$obj = new SuperClass(new Dependency1(new Dependency2()), new Dependency3());
Когда вы создаете все зависимости явно и явно передаете их в конструктор в самом начале, то есть без использования контейнеров и чего либо ещё.
какое отношение это вообще к Dependency Injection имеет?
Достаточно прямое, позволяет вместо кода выше писать подобное:
$obj = $locator->get('SuperService');
А под капотом будут внедрены нужные зависимости.
2) внедрение зависимостей, которые на самом деле не используются означают что у модуля низкая внутренняя связанность и его нужно разделить на отдельные модули. А еще есть lazy инициализация с проксями. Опять же вас никто не заставляет писать свой контейнер — юзаем готовые которые все это умеют и учитывают.
Согласен, но всегда есть граница здравого смысла. Городить прокси просто для того чтобы следовать паттерну, при том что сервис никогда не будет изменен? По-моему овчинка выделки не стоит. По крайней мере я предпочту повышение связности до определённой границы.
неверно. Основная суть что у вас есть один объект в системе, чья задача разруливание зависимостей. Никакой чуши про интерфейсы => реализации.
Это грубо, имеется ввиду что зависимости не черной магией разруливаются, а по определённым образом составленным правилам.
Толк в том что она есть и мы не пишем кучу фабрик. Они генерятся сами. А если вы посмотрите на популярные контейнеры — там в принципе в 80% случаев вообще конфигурацию описывать не нужно.
То есть решаем проблему, которой изначально не было? Сейчас во фреймворке вообще нет фабрик, они просто не нужны. И есть лишь один метод во всём фреймворке, который занимается созданием подобных объектов. autowiring это круто, но это работает не в 100% случаев, а в данном случае решает то, что не является проблемой изначально.
ленивая загрузка это не решение проблемы а ее симптом. Ну и про конфигурацию — тупой autowiring делается весьма просто.
Опять таки да, но проблемы может вообще не быть при альтернативном подходе.
Я пытаюсь донести, что при наличии единственной реализации чего либо в принципе, не вижу смысла в интерфейсах, DI, тащить что-то калибра PHP-DI (который, несомненно, крут, но по объему как 15% всего CleverStyle Framework) и так далее, поскольку это не имеет практического смысла. Гораздо удобнее иметь прямой вызов и автоматический вывод типов в IDE без каких-либо сторонних плагинов, да и производительность получится самая высокая. Если это можно в итоге протестировать и вместе оно хорошо работает — то почему нет?
Меня привлекает простота реализации, когда несколькими кликами можно добраться к строчкам, которые реально что-то делают, а не являются очередной абстракцией.
PSR поддерживается на уровне совместимости, к примеру, прослойка для PSR7, автозагрузчик аналогичен PSR4 по сути, просто есть несколько директорий с разными префиксами (cs\modules это модули в modules, а cs это системные классы в core\classes), тот же Composer можете использовать.
На счёт DI — нет и не планируется. Во фреймворке он не нужен, ибо он не компонентный и не предполагает свободной замены своих частей. А если вам нужен DI — вон сколько их готовых на GitHub, подключайте чего вам нравится, не вижу пока смысла что-то в этом плане навязывать.
На счёт паттернов — что конкретно вам не нравится? Буду благодарен если ответите конкретно с примером, а то выглядит как вопрос на собеседовании)
Это хорошо, но те же cookie можно сопоставить с изменяемым User-Agent при перезапуске (если они не чистятся при перезапуске, что достаточно неудобно), я бы предпочел не давать лишние биты информации в счётчик "подозрительности" пользователя. Лучше прикинуться самым обыкновенным среднестатистическим пользователем.
Как вы сопоставите разные IP с самым популярным User-Agent? Мне кажется, намного проще вычислить тех, кто во время сессии случайно меняет User-Agent, либо использует что-то вроде SeaMonkey вместо предпоследней стабильной версии Chrome или Firefox на Windows 7 или 10.
Не вижу принципиальных проблем в создании расширения, которое будет не рандомизировать, а периодически обновлять User-Agent на самый популярный в данный момент.
Я бы отформатировал иначе, но в целом достаточно приятный вариант, согласен.
Здесь в зависимость модуля одна строчка в
meta.json
(пример), и ещё одна если ещё нет зависимости от Composer (пример).Не вижу никаких проблем кроме того, что именованные подготовленные выражения не поддерживаются.
Пример из документации:
А здесь получится следующее:
Я просто выставлю оба примера рядом чтобы было наглядно видно что короче, а что нет. Хотя я бы сказал, что оно соразмерно практически 1 в 1, поскольку, повторюсь, в подобных ситуациях query builder прямо дублирует SQL синтаксис.
Согласен, но мы же говорим пока (имеется ввиду содержимое поста) о произвольных запросах. ActiveRecord слегка уровнем абстракции выше. Его вы, в конце концов, и здесь можете подключить при желании.
Для этого есть
cs\CRUD
иcs\CRUD_helpers
, которые позволяют вообще обойтись и без SQL и без конструирования запросов. Всё сводится к декларативному описанию структуры таблиц и вызовом одного метода.Пример в системном классе: структура, чтение, обновление данных.
В том то и дело, что в примерах Query builder не короче, а из автодополнения вы получите только методы. В то время как в SQL вы получите красивую подсветку синтаксиса, автодополнение SQL команд, в том числе тех, о которых Query builder не знает, а так же полей из реальных таблиц. Query builder ну никак здесь не выигрывает у SQL, даже близко. Это не учитывая того, что SQL может читать любой, а в синтаксисе конкретного Query builder-а и его ограничениях нужно ещё разбираться.
А здесь, простите, сколько строчек? Чем вам не читаем любезно раскрашенный IDE SQL?
Хорошо, давайте сравним. Для примера беру сниппет из документации:
А теперь CleverStyle Framework (полная аналогия):
Вы правда утверждаете, что на это нужно много нервов?
Дело в том, что абстрагирования там всего ничего, большинство запросов на 100% повторяют синтаксис SQL запросов. Как по мне, так проще сделать несколько простеньких конвертаций как в примерах, и дальше писать SQL.
На самом деле бывает больше двух запросов, тогда и больше строчек моэно сэкономить, и читать визуально проще. Пример 3х запросов.
Под капотом всё очень прямолинейно, но потенциально есть возможность объединять запросы и делать из нескольких запросов один запрос в БД, просто с этим есть некоторые сложности (
mysqli_multi_query()
ведет себя не совсем так, как хотелось бы, PostgreSQL не желает делать множественные запросы с серверными подготовленными выражениями, SQLite, по-моему, вообще множественные запросы не поддерживает).Сейчас активно используются emoji, и нужно быть готовым к тому, что такие символы попадутся в произвольном тексте.
Так что я бы предположил, что даже англоязычные разработчики его используют довольно часто.
Полагаю, имеется ввиду что PHP перекомпилировать для подключения модулей совсем не нужно, их можно подключать и отключать по необходимости.
Более того, mb_string достаточно популярный модуль в любом случае.
Как раз сегодня видел шутку в тему
Есть события, с их помощью вы можете делать любые алиасы, которые вам только заблагорассудится.
Если не указывать язык в URL — будут взяты к сведению заголовки запроса. К примеру, Accept-Language если это браузер, так же поддерживаются заголовки, которые отправляет Facebook, когда встраивает preview в ленте (ему почему-то не хотелось стандартный Accept-Language применять).
Далее если и заголовков нет никаких — используется язык, сконфигурированный в настройках системы.
Так же если пользователь зарегистрирован и в профиле явно указан язык — он так же будет иметь бОльший вес, чем язык в заголовках и чем в настройках системы.
Но это уже не совсем к маршрутизации относится, это многоязычность, с которой тоже много все интересного происходит)
Моки и стабы на используемый внешний код и вперёд. В инструментах для разработки самого фреймворка такие штуки уже есть готовые и активно используются, при желании можно любые собственные инструменты притащить.
Естественно, я имею ввиду что оно полностью ложится на конвенцию. Если понять несложный принцип, то всё сразу становится на свои места, не нужно изучать код и конфигурацию для того чтобы с почти 100% вероятностью узнать что будет происходить и где это искать.
Во фреймворке нет очевидной связи между определённым маршрутом и моделью, к примеру, статьи. Соответственно, нет простого способа в общем виде ассоциировать какие-то сущности с маршрутами и обратно. В последнее время всё больше перехожу на подход API + по сути отдельное приложение на фронтенде, которое работает с API. В этом случае пути всё равно придется генерировать не на сервере. В общем, нужно будет над этим подумать.
Приведу команду к вашему формату:
Не вижу какой-то фундаментальной разницы. В то же время, в Symfony не так очевидно, какой код отвечает за команду, а в CleverStyle Framework я не смотря в код знаю, что за запрос отвечает статический метод
cs\modules\System\cli\Controller::optimization_clean_cache()
.Почему именно так? Мне это показалось весьма логичным и удобным (как в HTTP запросах: метод и цель), а в контексте остальной маршрутизации даже в некотором смысле очевидно. Вы выполняете операцию (
clean_cache
) над сущностью или в контексте определённого пути (System/optimization
).Если бы мы создавали статьи из командной строки, то у нас были бы методы
get
,post
,delete
и сущностьArticles
. То есть в формате Symfony получаетсяarticles:get
, а здесьget:Articles
, хотя суть та же.Они и есть консольные команды, просто формат для унификации аналогичен остальной маршрутизации.
CLI команд сейчас всего несколько, к примеру, очистить кэш можно следующим образом:
Где
clean_cache
выступает в роли аналога HTTP метода, аSystem/optimization
в роли аналога пути страницы.Параметры командной строки превращаются в унифицированные параметры, доступные через привычный
$Request->query()
, аналогично параметрам после?
в URL:./cli help:System
выведет справку с доступными командами и форматом вызова (аналогично вызову./cli
без каких либо параметров).Увы, фреймворк ничего такого из коробки не предоставляет. Не знаю почему, но мне как-то даже не было нужно такое никогда. Можете уточнить для чего конкретно такое может быть нужно, что нельзя вручную сформировать ссылку? Интересно узнать сценарии использования.
Я имел ввиду подобное:
Когда вы создаете все зависимости явно и явно передаете их в конструктор в самом начале, то есть без использования контейнеров и чего либо ещё.
Достаточно прямое, позволяет вместо кода выше писать подобное:
А под капотом будут внедрены нужные зависимости.
Согласен, но всегда есть граница здравого смысла. Городить прокси просто для того чтобы следовать паттерну, при том что сервис никогда не будет изменен? По-моему овчинка выделки не стоит. По крайней мере я предпочту повышение связности до определённой границы.
Это грубо, имеется ввиду что зависимости не черной магией разруливаются, а по определённым образом составленным правилам.
То есть решаем проблему, которой изначально не было? Сейчас во фреймворке вообще нет фабрик, они просто не нужны. И есть лишь один метод во всём фреймворке, который занимается созданием подобных объектов. autowiring это круто, но это работает не в 100% случаев, а в данном случае решает то, что не является проблемой изначально.
Опять таки да, но проблемы может вообще не быть при альтернативном подходе.
Я пытаюсь донести, что при наличии единственной реализации чего либо в принципе, не вижу смысла в интерфейсах, DI, тащить что-то калибра PHP-DI (который, несомненно, крут, но по объему как 15% всего CleverStyle Framework) и так далее, поскольку это не имеет практического смысла. Гораздо удобнее иметь прямой вызов и автоматический вывод типов в IDE без каких-либо сторонних плагинов, да и производительность получится самая высокая. Если это можно в итоге протестировать и вместе оно хорошо работает — то почему нет?
Не хочется закончить написанием https://github.com/Herzult/SimplePHPEasyPlus либо https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition в реальной жизни.
Меня привлекает простота реализации, когда несколькими кликами можно добраться к строчкам, которые реально что-то делают, а не являются очередной абстракцией.
Это я часто слышу, попытался ответить на этот вопрос здесь
PSR поддерживается на уровне совместимости, к примеру, прослойка для PSR7, автозагрузчик аналогичен PSR4 по сути, просто есть несколько директорий с разными префиксами (
cs\modules
это модули вmodules
, аcs
это системные классы вcore\classes
), тот же Composer можете использовать.На счёт DI — нет и не планируется. Во фреймворке он не нужен, ибо он не компонентный и не предполагает свободной замены своих частей. А если вам нужен DI — вон сколько их готовых на GitHub, подключайте чего вам нравится, не вижу пока смысла что-то в этом плане навязывать.
На счёт паттернов — что конкретно вам не нравится? Буду благодарен если ответите конкретно с примером, а то выглядит как вопрос на собеседовании)
Это хорошо, но те же cookie можно сопоставить с изменяемым User-Agent при перезапуске (если они не чистятся при перезапуске, что достаточно неудобно), я бы предпочел не давать лишние биты информации в счётчик "подозрительности" пользователя. Лучше прикинуться самым обыкновенным среднестатистическим пользователем.
Как вы сопоставите разные IP с самым популярным User-Agent? Мне кажется, намного проще вычислить тех, кто во время сессии случайно меняет User-Agent, либо использует что-то вроде SeaMonkey вместо предпоследней стабильной версии Chrome или Firefox на Windows 7 или 10.
Не вижу принципиальных проблем в создании расширения, которое будет не рандомизировать, а периодически обновлять User-Agent на самый популярный в данный момент.