Symfony Как грамотно расшарить функцию?

Allality

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

Самый небольшой из них:
PHP:
// убирает http://, www и слэш из адреса сайта
$domenUrl = str_replace('http://', '', $domenUrl);
$domenUrl = str_replace('www.', '', $domenUrl);
$domenUrl = str_replace('/', '', $domenUrl);
Стоит ли делать отдельный класс для этого? Или сделать класс и в нем уже все подобные функции размещать?

Куда помещать файл с классом? Это будет отдельный бандл? Или в vendor?
 

shelestov

я тут часто
Пооффтоплю :)
PHP:
$domenUrl = str_replace('www.', '', $domenUrl);
Строка вида homewww.ru будет так же заменена.
 

keltanas

marty cats

Тугай

Новичок
Не зависимо от фреймворка:
Если блоки кода, зависят от конкретного приложения, то им место в потомке Application или в отдельном ApplicationHelper или раскидать по нескольким классам MyClass1lHelper, MyClass2lHelper, ...
Если блоки кода зависят от фреймворка, но пригодятся в других проектах, то им место в модулях (бандлах).
vendor для чужого кода, который не редактируется, кроме как через обновление на новую версию, в которой пофиксили какие-то баги.

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


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

Модуль - это нечто большее, формулировать не буду поищи в литературе, надеюсь смысл понятен. :)

(Посмотрел бегло доку по симфони, в примерах есть главный бандл, называют его Acme\HelloBundle.
Находися он в src/Acme/HelloBundle, namespace Acme\HelloBundle;
Создаешь катлог src/Acme/HelloBundle/Helper, сответственно namespace Acme\HelloBundle\Helper,
и class HelloHelper в файле src/Acme/HelloBundle/Helper/HelloHelper.php
и в него все такие куски.)
 

Allality

Новичок
keltanas, shelestov, пусть будет так:
PHP:
$parsedURL = parse_url($url);
$host = $parsedURL['host'];
if (substr($host, 0, 4) == 'www.'){
    $host = substr($host, 4);
}
Пока не суть.


Тугай, спасибо за развернутый ответ, но, честно говоря, я еще больше запутался. :) Почитал в интернет про то, как реализуют подобное в Symfony2. Кто-то пишет, что логику в Entity пихает, кто-то в контроллерах все держит, кто-то бандлы отдельные создает, кто-то Service Container делает, вы хелперы рекомендуете.

На сколько я понимаю, самое правильное именно для симфони - это использовать Service Container. Но документация по ним поганая. По крайней мере мне, как новичку не понятно многое, в то время как остальные пункты очень хорошо описаны в "The Book" к Symfony 2.3.

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

keltanas

marty cats
Есть достаточно подробный материал про контейнер в Symfony.

А то, куда помещать твой код зависит от того, где ты его используешь. Если тебе надо обрезать www. в представлении, воспользуйся фильтром twig-а. Если в контроллере, то сделай метод в контроллере. Если перед сохранением в БД, то в @ORM\PreUpdate / @ORM\PrePersist обработчик.

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

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

Allality

Новичок
Есть достаточно подробный материал про контейнер в Symfony
.
Service Container - это то же самое, что и Dependency Injection?

А то, куда помещать твой код зависит от того, где ты его используешь. Если тебе надо обрезать www. в представлении, воспользуйся фильтром twig-а. Если в контроллере, то сделай метод в контроллере. Если перед сохранением в БД, то в @ORM\PreUpdate / @ORM\PrePersist обработчик.
У меня есть еще несколько подобных функций, которые я бы хотел использовать в любом бандле, или хотя бы в любом контроллере одного бандла.

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

Сейчас так и сделал - метод в контроллере, но возникает проблема, я не могу использовать этот метод в других контроллерах или даже бандлах. Или могу?

Пример:

PHP:
class MagazinController extends Controller
{
    public function indexAction($magazinId, $otherUrl){
....
        // Создает Html список категорий магазина
        $spisokKategorij = $this->sozdatSpisokKategorij();
....
    }

    // Возвращает Html список категорий
    function sozdatSpisokKategorij()
    {

        $spisokHtml = '';

        foreach($this->kategoriiArr as $kategoriyaId)
        {

            if($kategoriyaId){ $spisokHtml .= $this->sozdatSsilkuKategoriiDlyaMagazina($kategoriyaId); }

        } // Конец foreach

        return $spisokHtml;

    } // Конец sozdatSpisokKategorij()

} // Конец контроллера


Но, мой совет остается прежним. Не валяй дурака. Тебе не надо обрезать www - это глупость.
Если предположить такую ситуацию, что тебе надо обработать URL, который вводит пользователь в форму, напиши фильтр для формы. Но, учти, что с www и без - это 2 разных сайта.
Конкретно эта функция мне нужна функция для того, чтобы отобразить "красивый" URL. Т.е. не "http://www.website.domain.ru/", а "Website.domain.ru". Сама ссылка, естественно, пойдет как "http://www.website.domain.ru/".
 

keltanas

marty cats
Ящик с шоколадками - это то же самое, что поедание шоколадок?

Все, что создает html, должно находится в шаблонах, а не в контроллере.

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

Allality

Новичок
Ящик с шоколадками - это то же самое, что поедание шоколадок?
Ничего не понял. Ушел читать ссылку.

Все, что создает html, должно находится в шаблонах, а не в контроллере.
Я понимаю, что делает этот твой код, но не понимаю, в каком случае собираешься это использовать?
Код идет по списку ссылок, полученных из БД, при этом при построении каждого элемента списка делается отдельный запрос в БД, видоизменяются некоторые данные и пр. Не представляю возможным сделать это с помощью Twig. Даже если это реально, то мне кажется более разумным хранить это все за пределами шаблона, для легкой смены шаблона в будущем.
 

Тугай

Новичок
Allality,
Что у нас за объекты, какие у них роли, как они взаимосвязаны, взаимодействуют? Все объекты нужно как-то классифицировать и организовывать.

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

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

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

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

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

Тугай

Новичок
.
Service Container - это то же самое, что и Dependency Injection?
...
процедурном PHP у меня был файлик functions.php, который я подключал туда, где требовалась хотя бы одна функция. Сейчас нечто подобное хочется сделать для Symfony2.
Разное.

PHP как язык никуда не делся, можно и по-старому include писать, в ООП теперь пишут use <mynamespace>.

создаем src/Allality/Functions.php, namespace Allality\Functions, class Functions {} и в нем все старое статическими методами.

Вот и все теперь где надо эти функции в начале пишем use \Allality\Functions и пользуемся. или не пишем use, a вызываем с полным именем \Allality\Functions::makeBetterUrl($str);
Автолоадер позаботится и выполнит include за нас.
 

hell0w0rd

Продвинутый новичок
В симфони все так устроено, что я не знаю в какой момент вообще может понадобится написать класс-хелпер. Вы должны четко понять в какой момент вам нужен этот хелпер и к чему относится.
Если к модели, если вы на пример хотите сохранять сообщение сырое + обработанное, чтобы каждый раз не обрабатывать в вьюхе - вешаете на событие записи обработку. Если вы хотите на выводе что-то делать - создаете функцию для твига и так далее.
Также не забывайте, что если вы хотите сделать функциональность запроса - создаете контроллер и делаете под-запрос.(хотя на мой взгляд это плохая практика, хотя и привествуется в симфони)
Внимательно прочитайте вот это, очень помогает понять как все внутри работает.
Также советую поизучать код вот этих ребят, уверен, что один из их бандлов вам когда ни будь точно пригодится)
И да, помимо обычной книги, есть cookbook - ее тоже необходимо прочитать, и вообще на сколько я понимаю в 2.3 версии core-разработчики сосредоточились на багах, тестах и документации, сейчас глянул на сайт - документации явно увеличилось)
Вот еще статья на тему di, sc, dic и всей дружной компании паттернов по хранению всех объектов в одном контейнере.
 

keltanas

marty cats
Allality
Может тебе покажется интересной возможность Embedding Controllers?
Но, опять же, ей не стоит сильно увлекаться. Это скорее костыль, чтобы сэкономить время. Но, если отчуждать код не будешь, можно и так))

И посмотри еще KnpMenuBundle
 

Allality

Новичок
Allality,
Можно смотреть как на хелпер - класс который помогает другим классам с урлами.
А в Симфони хелпери как реализовывать? Прочитал почти всю The Book, про хелперы не встречал ничего.

Основное это MVC паттерн - обработка запроса в веб страницу и все учасники этого процесса, в котором
классы решающие задачу предметной области занимают свое место среди прочих участников.
С view и controller у меня проблем нет. Сразу все понятно, про model понятно, что в них весь код по сути находится, но проблема в том, что в Symfony model включает в себя кучу всякой всячины.

создаем src/Allality/Functions.php, namespace Allality\Functions, class Functions {} и в нем все старое статическими методами.

Вот и все теперь где надо эти функции в начале пишем use \Allality\Functions и пользуемся. или не пишем use, a вызываем с полным именем \Allality\Functions::makeBetterUrl($str);
Автолоадер позаботится и выполнит include за нас.
Сделал так:

1. Создал php файл src\Al\Functions.php.
2. Добавил в AppKernel.php строку new Al\Functions(), чтобы загрузить класс.
3. Сам файл Functions.php:

PHP:
namespace Al;

class Functions {

    public function beautifyUrl ($url){
        $parsedURL = parse_url($url);
        $host = $parsedURL['host'];
        if (substr($host, 0, 4) == 'www.'){
            $host = substr($host, 4);
        }

        return $host;
    }
}
В итоге получаю ошибку при попытке воспользоваться методом beautifyUrl();

PHP:
use Al\Functions;
....
// Создает красивый URL помощью сервиса
$krasivijUrl = beautifyUrl($this->imUrl);

Fatal error: Call to undefined method Al\Functions::getName() in /pub/home/allality/imApp/bootstrap.php.cache on line 2312
Я не правильно загружаю или использую метод?

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

За ссылки спасибо. Обязательно прочитаю когда закончу с текущим чтивом. :)

Allality
Может тебе покажется интересной возможность Embedding Controllers?
Мне кажется решение с Embedding Controllers громоздкое какое-то. Возможно в будущем переделаю, когда буду четко понимать что к чему, пока мне кажется лучше оставить код, генерирующий ссылки тупо в контроллере.

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

hell0w0rd

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

keltanas

marty cats
Allality
Если тебе нравится работать с засраным глобальным контекстом, то стоит посмотреть в сторону фрейворков вроде Yii. Там ребята это активно юзают.
В итоге получаю ошибку при попытке воспользоваться методом beautifyUrl();
Похоже ты даже не дочитал в документации, что такое сервисы, и как подключить собственный класс как сервис?
Прочитал почти всю The Book, про хелперы не встречал ничего.
Нету в симфони такого понятия. Book - это только введение ;)

Если Embedding Controllers для тебя громоздко, то что тогда говорить об остальном? Я бы посмотрел, как ты будешь писать свои расширения для компонентов.

ЗЫЖ хватит ссать и начинай разбирать документацию. Мы можем подсказать, если что-то не понятно. Но, прочитать мануалы за тебя никто не сможет.
 

Тугай

Новичок
Метод должен быть статическим public static function beautifyUrl ($url){
тогда вызов так:
use Al\Functions;
$krasivijUrl = Functions::beautifyUrl($this->imUrl);

или если метод не статический, то
use Al\Functions;
$f = new Functions();
$krasivijUrl = $f->beautifyUrl($this->imUrl);

А полоучилось вот что, после: в AppKernel.php строку new Al\Functions()
файл заинкулдился но $krasivijUrl = beautifyUrl($this->imUrl); - вызов просто функции а не метода класса, вот и ошибка.
Получился, какой-то хак для инклуда ... Можно же просто написать include в AppKernel.php.

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

Тугай

Новичок
Еще немного поумничаю: :)
При разработке фреймворка, так или иначе был провден объектно-орентированый анализ (были открыты классы и объекты и составлен их словарь) и объектно-орентированное проектирование - (были избретены механизмы как это все должно работать).
Словарь и механизмы у разных фреймворков разные, хотя много паралелей и схожих изобретений.
Хелперы относится к словарю, в словаре Symphony такого нет, так же как нет, например, бандлов в других фреймворках.
Остается только читать снова и снова документацию и исходники пока не усвоится словарь и механизмы не станут понятными и очевидными.
 
Сверху