PHP Web Development - Отговори на въпроси от sli.do
Здравейте,
Виждам, че има няколко въпроса във sli.do, бих ги отговорил и на лекцията, но някои от тях може да изисквам дискусия, а не съм сигурен колко от задалите ги са присъствени, за това ще дам отговори и във форума, в случай че някой иска да се включи
Малко се връщам назад ... може ли да обясниш както точно прави ::class и как можем да го заместим? Пр: \Core\MVC\MVCContextInterface::class
Псевдоконстантата class връща името на класа (пълното му име, включително и namespace) като стринг. Може да се замести с ръчно писане на стринга, но рискуваме при промяна на името на класа, да не променим стринга, докато така извиквайки го директно от класа трудно може да стане същия проблем. В тази статия има примерче и как се държи PHPStorm в двата сценария.
Как да добавим къстъм клас и action във fromBulder-a пробвах с $builder->setAttributes() и нестана
За да се сложи на самия <form> таг action се ползва $builder->setAction(); а за да се сложи class, id или някакъв друг атрибут става през HTML-a (Twig-a): {{ form_start(form, {attr: {'class': 'my-form-class'}}) }}
Не разбрах защо се прави контролер, който не наследява предложения от Symfony. Бихте ли обяснили отново!
Истината е, че в 99% от случаите няма добра причина да се прави. Когато контролерът се изнесе като Service, в голяма част от случаите не може да наследява предложения от Symfony контролер. Понякога изнасяме контролер като Service когато се нуждае от това да му бъдат добавени (inject-нати) зависимости (dependency-та). Едното идва на цената на другото, аз лично предлагам да си наследявате стандартния Symfony контролер и когато дойде нуждата да се използва dependency injection-а на редица други обекти, то според ситуацията ще разберете трябва ли да спре да наследява държавния контролер или не.
Може ли в резюме да кажете отново как се завърта един цикъл от работата на един контролер в Symfony? .... Как и къде се извиква Контролера? Как се вземат данните за модела и как се извиква Вю-то?
На този въпрос ще отговоря и на лекция нагледно, но с няколко думи тук - първо един полезен линк - https://symfony.com/doc/current/controller.html. Symfony сканира всички класове в Bundle-ите и търси за анотации @Route и евентуално @Method, над методите им. Ако присъстват такива, то за Symfony това са годни за извикване методи при определени събития. Като последните се контролират от това какво пише в @Route и @Method. В първото е URI шаблонът в адрес бара (пр. /users/register), а второто е HTTP метод (пр. POST, ако анотацията не е написана по подразбиране е GET). Та, ако настъпи събитието /uses/register и метод GET се извиква съответният метод, който има тези анотации. Той приема потребителските данни през URL-а, тъй като е GET. Т.е. ако шаблонът беше /users/{id} (т.е. /users/ и нещо след него), то методът щеше да приема като аргумент променливата $id. Тя щеше да се напълни с данните равни на тези в адрес бара, например ако потребителят беше извикал /users/4, променливата $id щеше да има стойност 4. Вю-то от своя страна го управлява контролера, той в края на изпълнението си трябва да върне някакъв HTTP Response, например redirect или View. Последното се рисува на екрана чрез функцията render().
Ако данните, които трябва да приеме един контролер са множество, то шансовете са, че искаме да вземем данни от формуляр чрез метод POST. Чрез специалните типове формуляри, които предоставя Symfony, можем да зададем бизнес правила на полетата в нашия формуляр и да кажем кой обект трябва да се напълни при валидно изпращане на формуляра в configureOptions() -> 'data_class'. Това означава, че трябва методът, който ще се извика при изпращане на формуляра да приема Request обект, с който формулярът ще взаимодейства (handle) за да напълни нашия data_class (иначе казано Binding model или какъв да е друг модел, например от базата данни).
В тази статия от cookbook-а на Symfony има стъпка по стъпка създаване на регистрационен формуляр - http://symfony.com/doc/current/doctrine/registration_form.html
Как в една конзола стартирахте едновременно и сървъра и doctrine? За да подкарам doctrine ми се налага да го пускам в друга конзола, така ли трябва?
Не съм. Или съм спирал сървъра с CTRL+C, писал съм doctrine команди и съм го пускал пак, или съм го правил от два отделни прозореца :)
За login ползваме ServiceController, а за Register - DefaultController. Защо различни?
ServiceController - не би трябвало да сме ползвали такова нещо. Но нямаме добра причина да ползваме два контролера. В повечето фреймуърци нещата свързани с оторизиране и други неща свързани със сигурност са изнесени в контролер наречен SecurityController, защото отговаря за сигурността. Затова и login е там. Докато регистрирането на потребител е просто INSERT операция на някакъв обект (в случая потребител - User), затова е и в контролерът, който отговаря за потребителите (UsersController).
Надявам се да съм отговорил. Ще си ги говорим нещата и утре на упражнението. Ако има нещо - не с колебайте да пишете в тази тема или да отворите нова :)
Поздрави,
Иван
На 20-та страница в документа за упражнение започва това, което търсиш според мен. (най-долу)
Ето няколко примера: В ситуацията с нашия блог имахме статии (Article) и категории (Category).
Гледайки го от перспективата на статията, то много (many) статии могат да принадлежат на една (one) и съща категория. Тази връзка се нарича ManyToOne. Щом категорията е една, то трябва в нашия клас статия (Article) да имаме поле (наричано още навигационно пропърти) от тип Category.
Като код това в класа Article би изглеждало така:
Какво означава това? Това означава, че $category навигационното пропърти сочи към друга таблица от базата (targetEntity), която е тази с категориите (Category) и че в ответната таблица, навигационната колекция* (inversedBy, ще го разгледаме това по-късно) се казва статии "articles". Колоната в таблицата със статии (Article), която държи тази референция (която е външен ключ - foreign key) се казва "category_id" и реферира колоната (referencedColumnName) от таблицата с категории (Category), която се казва "id".
Щом много статии имат една и съща категория, това автоматична означава, че една (one) категория има много (many) статии. Голяма изненада, нали :) Едно и също нещо казах, но по два различни начина - просто второто е погледнато от переспетивата на категория (Category) - OneToMany.
Тогава в класът на категорията (Category) трябва да стои обърнатата (inversed) релация, така че когато си в категорията да имаш достъп до множеството статии, които тя има. Затова трябва някакво множество (колекция), и в категорията е нужно да има навигационно пропърти/колекция, което да се казва с името от inversedBy в статията, а именно "articles":
Тук казваме, че имаме масив от статии, сочат към друга таблица (targetEntity), която е статия (Article) и се връзват за полето $category (от по-горното каре)
От тук нататък, когато вземеш някоя категория, за да й вземеш статиите в нея трябва да поискаш този масив :)
Това важи и за self-referencing relationship, т.е. ако една категория е дъщерна на друга категория да речем.
-
Сега да разгледаме един друг случай. Отново от нашия блог. Тагове. Дадена статия (Article) може да бъде тагната с определено множество от тагове (Tag), но друга статия може да има същото множество или подмножество/супермножество на това множество, което означава, че много статии (Many) могат да имат много тагове (Many) от перспективата на статия (но очевидно и от перспективата на таг).
В такъв случай имаме следната ManyToMany релация в статията:
Тук казваме, че множеството от обекти от тип Tag (targetEntity) ще е нужно да се извади от междинна таблица наречена "articles_tags". В тази междинна таблица ще има две колони: първата ще се казва article_id и ще сочи към "id" колоната в текущата таблица (Article), а втората ще се казва "tag_id" и ще сочи към колоната "id" в нашето targetEntity (Tag).
Погледнато от перспетивата на таговете обаче - положението е същото - таговете от своя страна имат множество статии (в смисъл много статии може да са тагнати от тага "PHP" да речем). Значи отново трябва да имаме колекция в Tag:
Множеството от статии (колекцията от Article) сочи към Article (targetEntity) и се връзва за полето $tags в класа Article, като така знае, че трябва да ползва същите правила.
-
Да кажем, че трябва да намерим всички тагове на дадена статия. Тези навигационни колекции ни вършат много добра работа, защото няма нужда от вложени заявки или нещо от сорта, а е достатъчно да намерим статията и да използваме тази колекция: