Работа со связями моделей в Yii2 на примере каталога
Сначала хотел провести вебинар по связям ActiveRecord-моделей в Yii2 на основе урока о теории баз данных, осветив несколько тем из документации. Но получилась бы куча «воды» без практики. Так что решил показать на примере разработки каталога для интернет-магазина из реальной жизни:
Открыть на YouTube | Исходники примера
Приглашаю на следующие видеоуроки. Анонс и ссылку на эфир, как обычно, пришлю в отдельной рассылке по вебинарам:
И задавайте вопросы в комментариях. Заранее спасибо и до встречи в эфире!
МаксДимон, готовь курс по Yii2, тебе удается всё грамотно и понятно объяснять, а те курсы которые существуют явно нет то, миру необходим курс от профи =)
АлексДа, обсуждалось это на вебинаре. Дмитрий высказал мысль, что прологом к такому курсу будет ликбез по ООП. Считаю это неправильным. Такой подход превратит (ну может не превратит, но приблизит) к уже существующим бездарным. Не нужно тратить на это время. Тем, кому нужен Yii уже следовало бы изучить ООП. Если не изучили - материала как блох на собаке.
Дмитрий Елисеев> Тем, кому нужен Yii уже следовало бы изучить ООП.
А тем, кто изучил ООП, уже не пригодится курс ни по какому фреймворку.
Я под ООП имею в виду всю экосистему принципов, паттернов, антипаттернов, метрик технического и визуального качества кода. Применения принципов и паттернов по назначению именно как средств уменьшения сложности. То есть всех составляющих осознанного объектно-ориентированного мышления.
Это не просто ликбез про синтаксис, о котором говорите. Если напишете слово class и скинете в него свои процедуры (по требованию фреймворка или моды), то это никак не сделает вас ОО-программистом. А псевдоООПшного кода кругом ещё больше, чем упомянутых материалов.
АлексИ все же позволю себе не согласиться.
Что значит "изучить"? Год назад я начинал с c# (в отличии от PHP, си шарп чисто ООП язык). Кроме того, прочел пару книг по паттернам. В частности "Паттерны проектирования" Фрименов, и "PHP объекты, шаблоны и методики". И я как раз в той стадии, когда "уже Не, но еще и НЕ". Так вот, теория это одно, а практический разбор фреймворка, да еще и от такого хорошего специалиста и учителя - это совсем другое. Так что с фразой "кто изучил ООП, уже не пригодится курс ни по какому фреймворку" не согласен категорически.
makeloo – langworker.comЯ бы с удовольствием посмотрел бы хороший курс видео по Yii2 от Дмитрия. Если платно то подумал бы о том чтобы прикупить.
HiStO rIaNКогда вы уже смиритесь, что фреемворк это отдельный язык, ни на что не похожий. Если человек знает основной язык программирования, это не значит, что он легко освоит фреемворки! Тоже самое можно сказать, но ты же знаешь какой нибудь Perl зачем тебе уроки по php? Они никому не нужны.
Бесят такие умники, которые все знают, а нормально объяснить не хотят или не могут, и на критику только тявкают, типа, да вы обязаны все понимать, я же понял, вы прошли, изучили другие языки, вам не нужны знания и объяснения, ковыряйтесь, мучайтесь... По меньше бы таких появлялось на свет, может мир наконец то стал бы нормальным.
Столько уроков просмотрел, только единицы немного объясняют суть всего, там даже и 1% нету из всех "учителей".
Вы и всякие русаковы, которые создают уроки гавнокодинга, и не умеют вкладывать знания в голову, хотя обязаны. Это факт. Вместо того, чтобы кидаться на людей, научитесь учить, доставлять информацию для людей, и нормально разжовывать
Дмитрий Елисеев> Когда вы уже смиритесь, что фреемворк это отдельный язык, ни на что не похожий.
Фреймворки - это более профессиональный уровень, на который люди осознанно переходят со своих начальных самописов или с готовых CMS, когда готовый код их перестаёт устраивать.
Профессиональные фреймворки пишутся по продвинутым архитектурным принципам. Там уже нужно уметь профессионально программировать и разбираться в архитектуре, а не просто прочитать "PHP для чайников". Именно поэтому лезть "с полного нуля" сразу в меганавороченные фреймворки - это всегда туго.
А потом уже по принципам работы почти все фреймворки друг на друга похожи. Во всех есть абстракции над HTTP, маршрутизаторы, контроллеры, шаблоны и что-то для баз данных.
> Если человек знает основной язык программирования, это не значит, что он легко освоит фреемворки!
Об этом и говорю. Если человек уже "созрел" и знает не только язык, но и ООП, SQL, HTTP, общеупотребительные паттерны, практики и архитектурные принципы, то спокойно освоит фреймворки. А если не знает и знать не хочет, то бегает по всем статьям и форумам и кричит, что его никто не учит.
> не умеют вкладывать знания в голову, хотя обязаны
Да-да. Помню. Ещё президент вам обязан зарплату повышать и заставлять всех быть умными, здоровыми и богатыми. И жена обязана кормить, мыть полы и быть счастливой.
HiStO rIaNВот опять только отмазки, и ничего по делу. Фреемворк это язык. В него нельзя зайти зная тот же ооп. Я его достаточно хорошо знаю. Этот язык фреемворка надо учить, он не похож ни на один язык, и как раз вы обязаны учить, раз уж сами вызвались это делать. А вы только огрызаетесь, вместо того, чтобы понять факт!
Причем тут президент? Если уж так пошло, то это обязаность государства, раз уж они забрали все бесплатное и сделали своей собственностью. Каждый должен делать то, что умеет, и очень хорошо делать. А не так, как тут.
То что вам жопу все лижут, это не говорит о том, что вы хорошо разбираетесь. Если вы не видите фактов, это тоже не мои проблемы.
Я очень хорошо усваюваю информацию, но только тогда, когда она нормальная и разжевана. Ну или хотя бы написана по людски, для человека, а не как у вас.
Вы бы хоть перечитали что пишите. Ни имет файлов, ни имен папок, или это мы тоже должны ванговать и знать ?
Для начала разберитесь в себе, а потом говорите мне, что говорить и делать. У меня есть опыт обучения, только не в этой сфере, и я бы никогда не позволил себе подобных статей. Если я чувствую, что в статье что то не так, я ее никогда не выпущу.
После вашего "учения" только каша в голове! Даже бесполезная дока, и то больше информации дает, хотя что там черпать, когда ничего нету... Вот и задумайтесь
Дмитрий Елисеев> Фреемворк это язык. В него нельзя зайти зная тот же ооп. Я его достаточно хорошо знаю.
Значит обманываете себя и меня, что "достаточно хорошо знаете". Умение написать слово class - это ещё не "знание ООП".
> Этот язык фреемворка надо учить, он не похож ни на один язык.
Не путайте "язык" и "архитектуру". Web-фреймворки есть на PHP, Python, Ruby, Java, C# и остальных ОО языках. Все почти одинаково написаны по одним и тем же ООП-принципам и архитектурным паттернам. Сам язык здесь никакого значения не имеет.
> Даже бесполезная дока, и то больше информации дает, хотя что там черпать, когда ничего нету...
В этом и причина. Вы "осилили" только "бесполезную доку" по Yii и теперь ноете, что там ничего нет. Расширяйте кругозор. Просмотрите все доки по CodeIgniter, Yii, Phalcon, CakePHP, Laravel, Symfony, Zend, Slim, Zend Expressive... Тогда и придёт некое начально понимание. Разжовывать за Вас никто это не будет.
> И как раз вы обязаны учить, раз уж сами вызвались это делать.
Я не вызывался в статьях никого учить. Вам показалось.
HiStO rIaNАхаха, какие нахер доки? Мне делать нехер что ли? Это ваша обязаность объяснять, вы сами сюда пришли и сказали я учитель, так учите! Я не ною, а говорю факт! С которым согласятся все адекватные и нормальные люди, которые только начинают учить фреемворки.
И еще раз повторю, я очень хорошо понимаю ооп и php. В Yii2 могу многое создать, кроме этой убогой регистрации/авторизации/аунтификации, с которой рукожопы создатели намудрили.
Дмитрий Елисеев> Это ваша обязаность объяснять, вы сами сюда пришли и сказали я учитель, так учите!
Вам показалось.
HiStO rIaNЯ кажется сказал слушать факты а не кукарекать.... Мдаа, по хлебалу бы тебе навернуть пару раз, дрищ очкастый! Вообще уже берега попутал
HiStO rIaNЕще раз убедился в том, что подобных вам нужно выкидывать из простора интернета. Насоздают гавностатей обучающих, не могут ответить за свои слова и поступки, ходят и отнекиваются...
Все ясно, гавнокодеры они такие... Идите гавнокодте на готовых бесполезных мусорках
ОлегАхахах, боевой нытик )))
КорешьАдрес пиши сынок, уже выезжаю объяснять!
ИванНу ты и мудозвон :-) Лучше бы спасибо сказал за видео
Petr – elisdn.ruТы как кошка которая не может достать масло и говорить оно вонючая!
СергейСпасибо.
Как раз то что искал по поводу many-to-many связей. Делаю мультикатегории и не мог найти как это реорганизовывается на фреймворке, так что пример с тегами помог.
Добавлю что в миграциях, если выполняется более одного действия (создание таблицы и внешнего ключа или создание двух таблиц) в методе up() документация рекомендует использовать safeUp() для того чтобы если какое то действие зафейлится, то остальные действия откатятся. Может кому будет полезно.
И еще, Дмитрий, я не совсем понял за курсы. Вы проводите какие-то курсы по ООП, фреймворку или берете кого-то в ученики? Как можно попасть на обучение?
Дмитрий ЕлисеевMySQL поддерживает откат транзакций только для данных, так как автоматически коммитит транзакции при изменении таблиц или полей. Так что для неё safeUp бесполезен.
Дмитрий ЕлисеевГрупповых курсов пока не веду. Только репетиторствую, когда просят. Напишите на почту или в Контакты.
ДенисБыло сказано: "А тем, кто изучил ООП, уже не пригодится курс ни по какому фреймворку"
Дмитрий, не слушаейте никого, делайте курсы для новичков с ликбезом по ООП !
ЯромирСразу видно психологию школоты - никого не слушайте, а только меня слушайте. Книги читай, школяр!
СергейПривет. Подскажи плз какой дистрибутив linux лучше всего подходит для веб разработки по твоим предпочтениям?
Дмитрий ЕлисеевСтандартный стек Apache/Nginx, PHP и MySQL везде есть (а где нет - собирается из исходников), так что без разницы.
СергейЯ имел ввиду операционную систему на ядре линукс какую выбрать лучше (убунту, минт ....), может я не так выразился, вроде согласно википедии дистрибутив линукса и ОС это одни и те же вещи.
Спасибо за твои старания, изучении yii2 с твоими уроками стало намного проще!
Дмитрий ЕлисеевНу я и говорю, что на любом Линуксе PHP есть, так что без разницы.
ДенисUbuntu desktop operating system powers millions of PCs and laptops around the world :)
СергейХотелось бы иметь последнии версии php7 и mysql 5.7, но я так понял в линуксе есть сборки lamp и xampp и они еще на старых версиях
ДенисДля обучения тебе php>5.6 и mariadb 5.5 хватит надолго. А потом сам разберешься. Недорогого хостинга с поддержкой php 7 нужно ещё поискать). Сборки типа xamp проще ставятся, но для обучения я бы посоветовал ставить все отдельно, чтобы отличать хотябы что такое php от sql ))))
СергейНе понял с чего ты взял, что я не отличаю php от sql?
Aleksandr – aleks-r.comСергей, я так же как и Денис решил, что вы только собираетесь заняться обучением. Про линукс. На сервера чаще ставят серверный вариант Debian / Ubuntu / CentOS. В принципе, на начальном этапе линукс Вам не понадобится. Для разработки чаще используют виртуалку внутри виндоуса. Серверная и десктопная версия линукса отличается только набором программ для визуальной части, для серверов его вырезают. Самый популярный дистр на базе Убунту (та на базе Дэбиан) - Минт. В него установлено всё, что нужно пользователю без технических знаний. Мне приглянулся Elementary OS. Это дело вкуса. Если очень грубо - то чаще разные варианты линукса различаются "обоями да вариантами менюшек". Я про клонов на базе убунты. Научитесь работать с Bash, это то, что линукс делает линуксом для простого обывателя и пользователя.
ДенисДмитрий, подскажите, пожалуйста для особо понятливых. Честно, я посмотрел все 4 часа видео)
У меня есть форма для заполнения данных из двух моделей. Вопрос по экшену create.
Как сохранить данные одной модели, взять id первой сохраненной модели, и сохранить этот id в соответствующем поле второй модели? Приведенный код ниже у меня не сохраняет данные ни одной модели(
public function actionCreate() { $model = new Dailyinfo(); $info = new Information(); if ($model->load(Yii::$app->request->post()) && $info->load(Yii::$app->request->post()) && $info->save()&& $model->info_id=$info->id && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, 'info' => $info, ]); } }
Дмитрий ЕлисеевСначала $model->save(), потом $info->save().
ДенисПоменял весь порядок, добавил ещё несколько переменных для заполнения required полей, теперь $info сохраняется, а $model нет.
Мне кажется при сохранении $model не получает $info->id.
Дмитрий ЕлисеевСделайте print_r($info) и print_r($model), чтобы не казалось.
ДенисС вашей помощью, Дмитрий, заработало) Есть ли у вас замечания по моему коду, все таки стремлюсь постепенно к феншую, хотя понимаю, что ещё далеко) И думаю начинающим будет полезно.
public function actionCreate() { $model = new Dailyinfo(); $model->dept_id=1; $model->insert_date=date('Y-m-d'); $info = new Information(); $info->period_id=7; $info->forma_type=12; if ( $info->load(Yii::$app->request->post()) && $info->save() && $model->load(Yii::$app->request->post()) ) { $model->info_id=$info->id; $model->save(); return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, 'info' => $info, ]); } }
Дмитрий ЕлисеевЯ бы либо установку первоначальных значений перенёс в методы loadDefaultValues моделей. И вообще бы отдельную модель формы использовал.
ЕвгенийПодскажите пожалуйста как изучить ООП? Что читать и что смотреть? На что делать упор?
Дмитрий ЕлисеевПро кое-что расказывал здесь. А так, думаю, в YouTube что-нибудь уже есть.
А так может интенсив проведу в этом месяце по материалу, который ученикам рассказываю. Подпишитесь пока на рассылку. Если надумаю, то в ней сообщу.
Дмитрий ЕлисеевУговорили :) Запускаю интенсив.
Кабаченко ВикторОтличный вебинар.
Маленькое уточнение. Чтобы поместить в гриде количество записей в нескольких связанных таблицах, надо немного изменить ваше решение, указав DISTINCT в COUNT. Примерно так:
$query = Category::find() ->select(['{{%category}}.*', 'products_count' => new Expression('COUNT(DISTINCT {{%product}}.id)')]), 'materials_count' => new Expression('COUNT(DISTINCT {{%material}}.id)')]), ->joinWith(['products', 'materials'], false) ->groupBy('{{%category}}.id')
AndrewkhaВ коммите added product tabular attributes editor ошибка.
При таком коде в контроллере
foreach (array_diff_key($attributes, $values) as $attribute) { $values[] = new Value(['attribute_id' => $attribute->id]); }и таком выводе в представлении
все работает, если только id-шники атрибутов в соотв таблице идут подряд. Если же что-то вроде 1, 2, 5 то все поломается, loadMultiple не подберет значения там, где не совпадут индексы.
В контроллере надо
Дмитрий ЕлисеевСпасибо! Исправил.
СергейДмитрий, а можешь объяснить как в Yii2 сделать древовидные комментарии, без использования nested sets и т д? И чтобы можно было самому регулировать уровень максимальной вложенности.
Дмитрий ЕлисеевУ меня обычным parent_id. С выборкой всех одним запросом и с рекурсивным выводом. Максимальный сдвиг для вложенности делается простым CSS:
.comment { margin-left: 40px; } .comment > .comment > .comment .comment { margin-left: 0; }
AndrewkhaДмитрий, добрый день!
Слегка запутался в join, with и joinWith.
Скажи, пожалуйста, я правильно понимаю, что joinWith с параметром false - это аналог простого Join? или есть какие-то особенности?
Дмитрий ЕлисеевДа, аналог. Но join'у нужно вручную имя таблицы и колонок указать, а joinWith работает автоматически по имени связи.
Иван Васильев – www.nfsu-cup.comСпасибо за вебинар. Смотрел в записи. Очень много полезного для себя почерпнул из этого видео.
Ближе к концу видео, в CatalogController.php вы поменяли имя параметра действия с $tag на $name.
Тогда нужно поменять параметр при формировании массива ссылок на тэги в представлении _item.php.
Еще раз спасибо. Теперь собираюсь пересмотреть ваши более ранние вебминары.
Andrewkha – sportforecast.netДмитрий, приветствую. Нужна Ваша помощь :) Вопрос по работе с моделью и ActiveDataProvider.
Итак, имеется Junction table простого вида
id_user
it_tournament
points
первые два - внешние ключи для соотв таблиц.
Задача - для массива, содержащего id_tournaments получить все записи с максимальными points.
Что я сделал.
$query = []; foreach ($array as $one) $query[] = UsersTournaments::find() ->where(['id_tournament' => $one]) ->orderBy(['points' => SORT_DESC]) ->with('idTournament.country0') ->with('idUser') ->limit(1); $count = count($query); $toExecute = $query[0]; for($i = 0; $i < $count - 1; $i++) $toExecute = $toExecute->union($query[$i + 1]); return $toExecute;Если затем вызываю
все работает хорошо, т.е. выбирает именно записи с максимальным значением points для каждого id_tournament.
Но если же я этот $toExecute скармливаю в качестве свойства query для ActiveDataProvider начинаются чудеса.
А именно, для первого id_tournament (и только для него) выдает все имеющиеся записи, не только с максимальным значением points. Т.е. limit(1) в запросе не срабатывает. Для всех последующих id_tournament все хорошо.
Через дебаггер посмотрел, какой выполняется запрос и очень удивился.
Для первого id_tournament выполняется limit(20), такое ощущение, что ActiveDataProvider подменяет мой limit(1) своим дефолтовым для пагинации. Что интересно, для всех последющих, которые цепляются через union все хорошо, там limit(1).
Есть идеи, как это можно пофиксить?
Дмитрий ЕлисеевМожно попробовать все UNION обернуть в подзапрос в скобках. А так точно не знаю.
ДенисПодскажите, пожалуйста, как можно сделать так, чтобы при помещении в парку site\pages файла формата md (Markdown), он открывался как статическая страница?
Дмитрий ЕлисеевОткрыть через file_get_contents и скормить парсеру:
$this->render('page', [ 'content' => yii\helpers\Markdown::process($md), ]);
Sergey AverДмитрий подскажите почему после связи данных, я не могу обратиться к определенному свойству?
public function getIngredients() { return $this->hasMany(Ingredients::className(), ['id' => 'ingredient_id']); } public function getReceptIngredient($id) { return ReceptyIngredients::find() ->joinWith('ingredients') ->where(['recept_id'=>$id]) ->all(); } } $items = ReceptyIngredients::getReceptIngredient(1); foreach($items as $item) { print_r($item->ingredients); }выводит вот такой результат
Array ( [0] => frontend\models\Ingredients Object ( ... ) )А если обращаюсь к $item->ingredients->name
пишет что Trying to get property of non-object
Дмитрий ЕлисеевПотому что $item->ingredients - это тоже массив:
foreach ($item->ingredients as $ingredient) { echo $ingredient->name; }
Sergey AverА если я делаю связь многие ко многим, вообще выходит что выдает два массива, ингредиенты и мера веса, а мне надо чтобы был массив один где связаны две таблицы через промежуточную. Тогда надо другой запрос делать? В ручную через select??? Потому что результат моего запроса такой
А хотелось бы такой
Дмитрий ЕлисеевC "SELECT * FROM ..." только вручную, так как ActiveRecord результаты JOIN-ов не парсит.
ЯромирДмитрий, во-первых, большое человеческое спасибо за Ваш труд и Вашу отзывчивость. Во-вторых, не поделитесь Вашими планами на будущие вебинары? Помнится на интенсиве по ООП много людей высказывало желание увидеть вебинары по устройству symfony. Собственно, это было бы прямым развитием темы ООП про правильное структуирование клиентского кода в фреймворке через введение сервисного слоя, иньекции и т.д.Прекрасно было бы и в виде платного интенсива, хотя я отдаю себе отчет сколько времени уходит на подготовку к подобному событию.
А может быть вы планируете еще какие то вебинары по Yii? Поделитесь плиз, чего можно ждать, или наоборот не ждать. Спасибо.
Дмитрий ЕлисеевВсё планирую, но не всё успеваю. Так что как повезёт :)
РоманСпасибо за ваши труды, лучшего сайта по YII не находил))
Было бы интересно узнать как правильно делать URL каталога, н-р
domain/catalog/categoryParent/categoryChild/categoryChild/name_product
Дмитрий ЕлисеевСозданием своего класса-правила.
ВасяДмитрий подскажите как в виджете категорий в каталоге, вывести рядом количество постов?
Дмитрий ЕлисеевЕсли без иерархии, то echo $category->getPosts()->count().
ВасяЗаписал вот так:
'template' => '<a href="{url}" class="url-class">{label}</a>'.'<small class="badge">'.$category->getPosts()->count().'</small>,И количество запросов к базе выросло в два раза, было 8 стало 14, я делал другим образом, как в модели поиска categorySearch.php в файл categoriesWidget добавил public $posts_count;
Потом
$categories = Category::find() ->select(['{{%category}}.*', 'courses_count' => new Expression('COUNT(DISTINCT {{%post}}.id)')]) ->joinWith(['posts'], false) ->groupBy('{{%category}}.id') ->orderBy('name')->all();и вот так:
$items[] = [ 'label' => $category->name, 'template' => '<a href="{url}" class="url-class">{label}</a>'.'<small class="badge">'.$category->posts_count.'</small>', 'url' => ['main/category', 'id' => $category->id], 'active' => $this->category && $category->id == $this->category->id ? true : null, 'items' => $this->getItemsRecursive($categories, $category->id, $current), ];Выводиться нормально и запросов 8, так нормально или можно как то по другому?
Дмитрий ЕлисеевНормально. Мы так и делали в админке в гриде категорий в этом вебинаре. Ещё учтите, что с древовидными категориями в виджете нужно считать суммарное число постов в подкатегориях.
ВасяПодскажите пожалуйста как это сделать?
Дмитрий ЕлисеевЛибо завести поле total_count и при каждом изменении постов его пересчитывать, либо каждый раз рекурсивно суммировать по parent_id, либо перевести базу на Nested Sets и считать COUNT по массивам id вложенных категорий.
hajdar dushkuIn root hierarchy it shows 0 posts, how we could sum with root hierarchy also?
АлександрДмитрий, спасибо за очередной качественный вебинар.
Для себя хочу уточнить один момент.
В миграциях вы создаете индексы так же для полей, на которых так же создаете FK на другое поле (или другое поле другой таблицы). Для postgres согласен. Но, ведь в mysql при создании FK автоматически создается и индекс.
Дмитрий ЕлисеевПросто стараюсь делать миграции, универсально работающие и на MySQL, и на PostgreSQL.
Виталий БахурЗдравствуйте Дмитрий. Хотел бы спросить а как сделать подгрузку атрубутов динамически и на лету, чтобы и валидация была. Т.е смысл такой заходишь создаешь товар, в нём указываешь категорию и у нас для каждой категории подгружает свои атрибуты. Как такое реализовать? я так понимаю в сторону behaviors надо смотреть?
Дмитрий ЕлисеевЛибо возиться с $('#form').yiiActiveForm('add', ...), либо сделать форму двухшаговой, где на первой странице выбираем категорию, а на второй уже запоняем всё остальное.
Виталий БахурСпасибо за совет. Ну двухшаговую делать не буду, а вот в сторону $('#form посмотрю')
Олег – milalice.ruЗдравствуйте, Дмитрий. Я переделываю на Yii2 свой интернет-магазин (хочу перейти с Джумлы), но возникла сложность с выбираемыми пользователем свойствами товара (цвет и размер). В карточке товара выводятся несколько размеров и несколько цветов, но в наличии есть, например: товар 48 размера - белый, а 50 размера - красный. Подскажите, пожалуйста, как сделать связанные свойства товара: размеры со своими цветами? Как правильно создать таблицы в БД и какие прописать связи между нами?
АлексНикак не могу понять, как могу быть записи типа $category->parent->name или 'parent.name'. Что это вообще такое? В таблице категории у нас нет поля parent. Или 'parent.name' - что это? у нас нет ни таблицы parent, ни связи такой.
АлексПосмотрел исходники. Понял в чем дело. Не знал что $this->hasOne() и $this->hasMany() можно на тот же класс модели делать.
Андрей КушнаревЗдравствуйте Дмитрий, нашел ошибку не проявляющуюся на присутствующем наборе данных, пишу тут потому как смог решить ее только частично:
В админке в просмотре продуктов при сортировке по имени категории по факту происходит сортировка по category_id.
Если в ProductSearch поменять
$query = Product::find()->with(['tags'])->joinWith(['category', 'productTags'], false); ... $dataProvider->sort->attributes['category_id'] = [ 'asc' => ['{{%category}}.name' => SORT_ASC], 'desc' => ['{{%category}}.name' => SORT_DESC], ]; ...Теперь сортировка работает правильно (по имени), но возникла проблема с фильтрацией по одноименным полям которые присутствуют в product и category(в частности id и name):
Error Info: Array ( [0] => 23000 [1] => 1052 [2] => Column 'name' in where clause is ambiguous )как можно решить эту проблему?
Андрей КушнаревРешил таким способом.
В ProductSearch, то что было изменено выше так и осталось и еще поменял:
$query->andFilterWhere([ '{{%product}}.id' => $this->id, 'category_id' => $this->category_id, 'price' => $this->price, 'active' => $this->active, '{{%product_tag}}.tag_id' => $this->tag_id, ]); $query->andFilterWhere(['like', '{{%product}}.name', $this->name]) ->andFilterWhere(['like', 'content', $this->content]);Вопрос - а можно ли сделать тоже самое, но при этом не использовать имена таблиц БД, а использовать связи прописанные в модели AR?
Дмитрий ЕлисеевМожно вручную указать псевдонимы для каждой таблицы. А сам фреймворк имена связей в запрос не подставляет.
Андрей КушнаревА можно поподробнее и с примером? А то что-то не совсем понятно.
Дмитрий ЕлисеевУказать псевдонимы:
$query = Product::find() ->from(['product' => Product::tableName()]) ->joinWith('category category');И использовать псевдоним 'product.id' вместо имени таблицы '{{%product}}.id'.
Андрей КушнаревСпасибо, вот как раз то что нужно было, не понимал как прописать псевдоним для продуктов...
ShvarzСпасибо за урок , очень классно, разобрался уже связал 3 таблицы... Но теперь все, копипаст закончился, теперь думать самому приходится.
Таблица users связана с таблицей event, мне нужно чтобы при создании нового event я вручную не вбивал айдишник пользователся. скорее всего нужно в представлении _form, вместо
сделать
но у меня с синтаксисом беда помогите ....
Дмитрий ЕлисеевЕсли пользователей мало, то можно выпадающим списком:
ShvarzНет, Дмитрий вы не поняли меня, мне нужно чтобы он выбирал текущего залогиненного пользователя и сразу добавлял в поле user_id в таблице event.
т.е кто создал событие того айдишник и добавляется, что это он создатель.
ShvarzТут походу функцию надо писать , хотя бы пример какой-нибудь....
ShvarzYii::$app->user->id - полчение текущего айди пользователя
и потом нужно в представлении _form или create записать в поле user_id это значение
Дмитрий ЕлисеевВпишите в контроллере:
и всё.
Shvarzспс, я был близок, во второй строке у меня небыло в начале $model...
Жесть столько времени убито, надо синтакисис учить...
ShvarzВопрос еще один созрел: Если я создаю,допустим событие в котором нужно добавить файлы.
Как загрузить сразу файлы в момент формирования события? (хочу хранить файлы, логи все в бд)
Какие способы выбрать и правильно ли я вообще мыслю?
Способы:
1)сразу создавать событие с пустыми параметрами, но только чтобы был айди события и сразу загружать файлы к событию?
2)Или сделать временную таблицу туда записывать файлы ?
ShvarzКогда я нажимаю на кнопку создать событие т.е actionCreate событие уже в базе создается с айдишником или нет? Извините за глупые вопросы понимаю как понимаю ...
Дмитрий ЕлисеевАйдишник добавляется после save(). Если же нужно загрузить по Ajax до сохранения модели, то обычно грузим всё во временную папку и сохраняем имена файлов в сессию. А в момент сохранения уже перемещаем эти файлы куда надо.
ShvarzСпс, для начала пойду путем создания 2-ой формы, а потом когда всю структуру выведу буду оптимизировать...
Shvarzтаблица Event связана с таблицей User:
$this->addForeignKey('fk-event-user_id','{{%event}}','user_id','{{%user}}','id','CASCADE','RESTRICT'); $this->addForeignKey('fk-event-isp_id','{{%event}}','isp_id','{{%user}}','id','CASCADE','RESTRICT');В представлении event\index вывожу вместо параметров 'user_id' и 'isp_id' следующее:
[ 'attribute' => 'user_id', 'value'=>function(Event $event){ return $event->user->username; } ], [ 'attribute' => 'isp_id', 'value'=>function(Event $event){ return $event->user->username; } ],Он выводит имена , но если потом сравнить с id , то неправильно , одного айди вообще не выводит , вместо него повторяет имя старого, что я не так ?
ShvarzТ.е у меня есть айди создателя и айди исполнителя, и привязаны они к одному айди из таблицы User.
Дмитрий ЕлисеевНу так и выводите из разных:
Или проще:
[ 'attribute' => 'user_id', 'value' => 'user.username'; ], [ 'attribute' => 'isp_id', 'value' => 'isp.username'; ],
ShvarzЗаработало,спс. Ток я не вкурю , почему так?
Shvarzтипо отбрасывается _id ?
Дмитрий ЕлисеевВ модели Event генерируются методы getUser() и getIsp() с hasOne либо hasMany. Это и есть связи $event->user и $event->isp.
ShvarzАга догнал, спс...
ShvarzCоздал функцию ,которая возвращает фамилию имя отчество и логин , но захотел еще лучше , чтобы возвращало только первый символ отчества
но он какой -то фигурный символ выводит вместо первой заглавной буквы отчества.
Как я понимаю нужно в представлении через аррайхелпер , но там тоже выведет только полностью функцию...
Получается либо в модели как-то парсить , либо в представлении через хелпер все параметры перечислять....
$event=Event::find(), $ggstr=substr($event->user->last_name,0,1), 'value'=>ArrayHelper::getValue(function (Event $event) { return $event->user->name . ' ' . $event->user->last_name; })
ShvarzНашел проблему, кому интересно решается через mb_substr
ShvarzХочу сделать так :gри создании заявки ( Event), я выбираю из >dropDownList отдел( Department), после выбора отдела у меня выбирается 1 исполнитель только из этого выбранного отдела.
Но не работает , брал материал:
1) http://quabr.com/34989819/yii2-dependent-dropdown-list
2) https://www.youtube.com/watch?v=ZepxKw8VA7w
в итоге переделал под свой вариант и вот что получилось , но не работает при выборе исполнителя...
ProfileController:
public function actionLists($id) { $countProfile=User::find()->where(['department_id'=>$id])->count(); $profile=User::find()->where(['department_id'=>$id])->all(); if ($countProfile > 0) { foreach ($profile as $user1) { echo "<option value='" . $user1->user_id. "'>" . $user1->fioName . "</option>"; } } else { echo "<option> - </option>"; } }event-form:
<?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?> <?php $dataDEP = ArrayHelper::map(Department::find()->asArray()->all(), 'id', 'name'); echo $form->field($model, 'dep_id')->dropDownList($dataDEP, [ 'prompt'=>'Selected department', 'onchange'=>' $.post( "'.Yii::$app->urlManager->createUrl('profile/lists?id=').'"+$(this).val(), function ( data ){ $( "select#profile-dep_id" ).html(data); }); ']); echo "<br>"; $dataUSER = ArrayHelper::map(User::find()->asArray()->all(), 'id', 'fioName'); echo $form->field($model, 'isp_id')->dropDownList($dataUSER, [ 'prompt'=>'Selected user', 'id'=>'dep_id'] ); ?>Что не так , думаю либо со связями проблема, либо еще что-то в модель нужно записать ...
Помогите.....
ShvarzВсе сделал, без всяких kratikov =)
ShvarzСнова я. Привет Дмитрий.
public function actionSubmission() {$comment=new Comment(); $comment->name=$string; $comment->user_id=Yii::$app->user->id; $comment->event_id='71'; $comment->save(); return $this->render('submission', [ 'stringHash' => $string, ]); }эта функция в EventController , как мне получить значение текущего айди сообщения , чтобы ($comment->event_id='71 ) не присваивать вручную?
Заранее спасибо...'
ShvarzТ.е. У меня есть модель Event и модель Comment, через аякс я добавлю в моделе евент комментарии
Дмитрий Елисеевpublic function actionSubmission($id) { ... $comment->event_id = $id; ... }И передавать ?id=71 в адресе из Ajax.
ShvarzНе это все я пробовал , результат тот же , может я не правильно объяснил , сейчас попробую точнее.
Я использую не чистый аякс а pjax:
представление :event- submission
...... <?php Pjax::begin(['enablePushState' => false]); ?> <?= Html::beginForm(['event/submission?id=71'], 'post', ['data-pjax' => '', 'class' => 'form-inline']); ?> ........... <?= Html::endForm() ?> </div> <div class="col-md-5"> <h3><?= $stringHash ?></h3> <?php Pjax::end(); ?>Фишка в том, что если я не в view/71 , а допустим в view/63 захожу и там пытаюсь добавить , то он мне опять добавляет комментарий с id_event=71.
['event/submission?id=$id'], как-то так надо , он не видит в какой текущий вием я зашел , как мне определить в каком я виеме нахожусь, чтобы он вместо 71 поставил уже 63..
Shvarzpublic function actionSubmission($id) storm подчеркивает $id , что аргумент отсутствует, как мне его правильно передать в представлении ?
ShvarzИ если можно скиньте ссылку на примеры рабочие , где чистый аякс, потому -что походу мне придется кучу функций писать , откуда черпать материал , заранее спасибо...
Дмитрий Елисеев
ShvarzИхаааа, заработало, только надо там не равно , а (указатель) => и еще добавить
Спасибо Дмитрий , без тебя хз сколько еще бы промучился...
Олег – milalice.ruЗдравствуйте, Дмитрий. Я переделываю на Yii2 свой интернет-магазин (хочу перейти с Джумлы), но возникла сложность с выбираемыми пользователем свойствами товара (цвет и размер). В карточке товара выводятся несколько размеров и несколько цветов, но в наличии есть, например: товар 48 размера - белый, а 50 размера - красный. Подскажите, пожалуйста, как сделать связанные свойства товара: размеры со своими цветами? Как правильно создать таблицы в БД и какие прописать связи между нами?
Это вопрос я уже задавал месяца 2 назад, но ответа не получил. Интересно, получу ли я его сейчас?
Или все так сложно?
Дмитрий ЕлисеевЗдравствуйте. Добавьте таблицу availability с полями product_id, size_id, color_id и count. Будут обычные связи к первичным ключам.
ОлегЗдравствуйте, Дмитрий. Делаю этот урок за Вами. и У меня не получается вывод хлебных крошек со вложенными категориями. Выводится в крошках только: Главная / Catalog. Вот код, который я воспроизвел из урока.
$this->title = $category->name; $this->params['breadcrumbs'][] = ['label' => 'Catalog', 'url' => ['idex']]; $crumbs = []; $parent = $category; while ($parent = $parent->parent) { $crumbs[] = ['label' => $parent->name, 'url' => ['category', 'id' => $parent->id]]; echo $crumbs; } $this->params['breadcrumbs'] = array_merge($this->params['breadcrumbs'], array_reverse($crumbs)); $this->params['breadcrumbs'][] = $this->title;
ОлегРазобрался, не передал в вид $category из экшена.
АлександрПодскажите как отсортировать модель по связной таблице с помощью yii\data\Sort
Дмитрий ЕлисеевС 3:02:09 на видео.
АлександрСпасибо помогло)
Александр ИсаевДмитрий, как можно вывести только категории, в которых есть продукты с определённым тегом?
Дмитрий Елисеев
Сергей СкапаЕсть три таблицы: для групп, для установок и для оборудования.
В третью передаются id из первых двух, делая вложенность.
Фильтрация идет:
//'groups_id', [ 'attribute' => 'groups_id', 'filter' => Groups::find()->select(['name', 'id'])->indexBy('id')->column(), 'value' => 'groups.name', ], //'lines_id', [ 'attribute' => 'lines_id', 'filter' => Lines::find()->select(['name', 'id'])->indexBy('id')->column(), 'value' => 'lines.name', ],Но есть проблема, как отсеять часть записей 'lines_id' после выборки в первом фильтре 'groups_id'
Есть ли решение без подключения сторонних модулей?
Дмитрий ЕлисеевLines::find()->alias('t')->andWhere([ 'exists', Posts::find()->alias('p')->andWhere([ 'p.lines_id' => new Expression('t.id'), 'p.groups_id' => $searchModel->groups_id ]) ])->select(['t.name', 't.id'])
Сергей СкапаДмитрий, подскажите почему может ругаться на класс Posts?
Дмитрий ЕлисеевПотому, что его надо на ваш класс заменить.
Сергей Скапаполучил ошибку: "array_merge(): Argument #2 is not an array"
'attribute' => 'lines_id', 'filter' => Lines::find()->alias('t')->andWhere([ 'exists', Equipments::find()->alias('p')->andWhere([ 'p.lines_id' => new Expression('t.id'), 'p.groups_id' => $searchModel->groups_id ]) ])->select(['t.name', 't.id']),
Дмитрий Елисеев...->indexBy('id')->column(),
Сергей СкапаДмитрий, спасибо работает:
[ 'attribute' => 'lines_id', 'filter' => Lines::find()->alias('t')->andWhere([ 'exists', Equipments::find()->alias('p')->andWhere([ 'p.lines_id' => new Expression('t.id'), 'p.groups_id' => $searchModel->groups_id]) ])-> select(['t.name', 't.id'])->indexBy('id')->column(), 'value' => 'lines.name', ],но из-за того что строка: 'value' => 'lines.name', не обрабатывается в index.php -> не подставляется значение. Если её убрать то id подставляет. Что делаю не так?
Дмитрий ЕлисеевВидимо проблема со связью.
Сергей Скапакак я понимаю, то значение 'value' = null, но как его правильно передать?
СергейДмитрий, при добавлении к Товарам Тегов с использованием checkBoxList Вы рекомендуете выполнять валидацию путем:
Однако, checkbox всегда отправляет данные типа string, и потому данный валидатор ВСЕГДА выдаст ошибку.
Подскажите, пожалуйста, как правильно проверить тип данных в таком случае?
Дмитрий ЕлисеевЕсли нужен обязательный checkbox, то:
СергейНа странице редактирования товара есть checkboxList 'tagsArray', который возвращает массив tagsArray, содержащий id-шники тегов данного товара.
Мне нужно убедиться, что из формы пришли именно integer. Вы в этом вебинаре рекомендуете использовать ['tagsArray', 'each', 'rule' => ['integer']], однако checkbox в значении value всегда возвращает string. То есть, правило валидации в таком виде вернет ошибку - ведь из формы всегда будет приходить массив string.
Я вышел из положения, применив правило валидации ['arrayTags', 'each', 'rule' => ['match', 'pattern' => '/^\d+/']], но мне интересно, есть ли, возможно, другое, концептуально правильное решение.
Спасибо.
Дмитрий Елисеев> правило валидации в таком виде вернет ошибку.
Не вернёт, так как все данные из формы всегда приходят как string и данный валидатор это учитывает.
АлексейОтличный вебинар. Спасибо! Много полезной инфы.
Как модифицировать данный запрос, чтобы считалось количество товаров не только в самой рубрике, но и для родительской рубрики считалось количество товаров во всех дочерних рубриках?
$query = Category::find() ->select(['{{%category}}.*', 'products_count' => new Expression('COUNT({{%product}}.id)')]) ->joinWith(['products'], false) ->groupBy('{{%category}}.id') ->with(['parent']);
Дмитрий Елисеев1) Получить массив $ids из id текущей и всех её дочерних рубрик.
2) Посчитать Product::find()->andWhere(['category_id' => $ids])->count().
Олегпривет.
есть такой вопрос по связям в Yii2
у меня есть 2 связи:
public function getPlaceWashere() { return $this->hasMany(PlaceWashere::className(), ['place_id' => 'id']) ->where(['washere' => 1]); } public function getPlaceWashereUser() { return $this->hasMany(User::className(), ['id' => 'user_id'])->via('placeWashere'); }в ходе выполнения запроса с жадной загрузкой связи PlaceWashereUser т.е. ->with(['placeWashereUser'])
происходит выполнение 2 запросов
первый выбирает ключи
второй - уже то что и хочется - подставляя ключи в условие ... IN (.. ключи ..)
такое поведение мне не подходит - т.к. связей очень много здесь PlaceWashere::className()
существует ли возможность указать что ключи выбирать не нужно - а нужно использовать первую связь как подзапрос?
спасибо за ответ
Дмитрий ЕлисеевYii2 всегда подгружает связи по IN (...).
Олегспасибо. уже разобрался )
$this->hasMany(... можно и подзапросы делать используя ActiveQuery который веренет геттер
АлексейЗдравствуйте. Мне нужно при создании материала (например статья) загрузить изображения через ajax и привязать их к этому материалу, но у материала пока нет id, к которому я собираюсь привязывать картинки. Для пользователя заполнение полей материала и загрузка картинок должны быть на одной странице. Я решил сделать так:
Пользователь жмет "добавить статью", срабатывает экшн создания пустой записи статьи в базе и сразу перебрасывает на редактирование этой статьи. Таким образом id уже есть, а пользователь думает, что он создает новую статью хотя на самом деле уже редактирует.
Подскажите, может есть какой-нибудь правильный вариант привязки изображений?
Дмитрий ЕлисеевЕщё можно сохранять файлы во временную папку и записывать их имена в сессию. А при сохранении статьи уже перемещать в нормальную папку и привязывать по id.
АртемЗдравствуйте, Дмитрий. У меня такой вопрос, если допустим у меня есть в продуктах столбец Category_name - это та категория, к которой мне нужно привязать товар. Как можно по имени Category_name связать товар с категорией?
Дмитрий ЕлисеевМожно в миграции добавить поле category_id и выполнить запрос UPDATE products p SET p.category_id = (SELECT c.id FROM categories c WHERE c.name = p.category_name).