Слабое место ??

Всем привет, почувствовал на собственной шкуре, либо я чего-то не знаю либо это крайне проблемное место Yii.

Собственно о чем это я…

Сохранение связанных данных. Я балдею от механизма relations это все очень круто избавляет от написания больших джоинов и т.д. имеет красивый вид… НО это никак не помогает делать запись!

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

Во-первых, рассмотрим ту же ситуацию что и в кукбуке надо сохранить к примеру комманду и к примеру 10 ее членов

Хорошо ли делать так как в кукбуке ? при сохранении 10ти ее членов будет сделано 10ть инсертов ??

Второе, как быть с валидацией в случае если имя игрока не проходит валидацию? Надо бы не дать сохранить имя комманды если имя игрока не проходит валидацию, ну тут я думаю должны помочь транзакции… и все же как Вы подобные вопросы решаете…

Ну и самое интересное надо же ведь иметь возможность не только создать такую вот комманду но и редактировать, причем надо иметь возможность редактировать одномоментно и название команды и всех ее игроков…

Задача кстати крайне распостраненная.

Я думаю большинство вопросов было бы снято если б фреймворк пользовался своими же relatuibs’ами и для сохранения а не только для чтения.

За ошибки не серчайте плохо уже видно устал :(.

Надеюсь на Вашу помощь… спасибо.

А еще было бы хорошо, если бы фреймворк кофе мне по утрам варил :)

Пожелания ваши конечно хорошие, но не все же сразу. Вот прочитает кто-то ваш пост, напишет соответствующее расширение и будет все тип-топ :rolleyes:

Извините, а с каким фреймворком вы работали что он это дело облегчал или выполнял сам?

Я б тоже с ИИ хотел познакомится… А вобще идея нормальная, может кто выделит время на подобное расширение. Хотя если тебя не устраивают 10 инсертов то думаю лучше смотреть в сторону DAO а не AR

Ну молодцы - спасибо Вам… пришли поржали, поумничали…

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

Я в принципе не про ИИ говорил а про то что если это не реализовано в фреймворке, то как это решают профи ?

Я про, к примеру, форму редактировани связанныъ данных …

или про валидацию …

Как сохранять написали, но то что написали ведь это чистой воды поделка.

Ладно фиг с ним с этим большим количеством инсертов, меня больше интересуют вопросы чуть выше…

А кофе кстати я не пью.

Спасибо.

Я бы просто делал интерфейс, где на одной странице отображаются данные о команде в общем и о каждом игроке. На яваскрипте написал бы возможность добавления/удаления новых игроков. А внизу страницы большую кнопку "Сохранить".

В обрабатывающем экшене сделал бы валидацию такую какую надо и сохранение.

Все, на самом деле ничего сложного. Делал подобное уже пару раз в разных проектах. Реализация занимает максимум 3-4 часа.

p.s. И не использовал бы никакие relations при сохранении. Там и связывать то практически нечего.

шапкозакидательство!

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

  2. Интересно боольшая кнопка сохранить отличается чем то от маленькой ? что это ??

  3. отличный ответ сделал бы валидацию такую как надо… супер! :slight_smile: Лучше ж ничего не написать правда ?

  4. конечно сложного вообще ничего нигде нет … так всегда говорят люди которые … ладно не буду продолжать.

  5. Только подумайте если бы можно было бы в месте с атрибутами массово присвоить и связанные данные а фреймворк бы уже сам все распихал бы по полочкам … что тоже бы не использовали бы ?

  6. Какая нахрен полезность вашего ответа ?? Если мы на форуме где все как бы помогают друг другу … то в чем ваша помощь в этом ответе ? Типа я крутой программер и сделал бы все это раз плюнуть ?? ну так мой ответ в самом первом слове. читайте его чаще что бы раньше юношеский максимализм проходил.

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

Это кстати огромная проблема данного фреймворка отсутствие жизненных примеров в кукбуках и особенно в апи… нету примеров … наверное форум для этого и создавался что бы восполнить недостатки … ?

spk, совета на форуме спрашивают, а не требуют. И пытаются конкретизировать свой вопрос, если ответ не получен или неясен сразу.

Если тебя спасет, могу дать пример своего кода:




    protected function processForm($project)

    {

        if(isset($_POST['Project']) && !isset($_POST['cancel']))

        {

            $new = $project->isNewRecord;

            

            if (!$project->isNewRecord) {

                $part = $project->part;

                $order = $project->order;

            }

            $project->attributes=$_POST['Project'];

            if ($project->isNewRecord)

            {

                $sql = 'SELECT MAX(`order`) FROM `' . Project::tableName() . '` WHERE part = :part';

                $command = Yii::app()->db->createCommand($sql);

                $command->bindParam(":part", $project->part, PDO::PARAM_STR);

                $project->order = $command->queryScalar()+1;

            }

                

            if (!$project->isNewRecord && ($part != $project->part))

            {

                $sql = 'SELECT MAX(`order`) FROM `' . Project::tableName() . '` WHERE part = :part';

                $command = Yii::app()->db->createCommand($sql);

                $command->bindParam(":part", $project->part, PDO::PARAM_STR);

                $project->order = $command->queryScalar()+1;

                

                $sql = "UPDATE `" . Project::tableName() . "` SET `order` = `order`-1 WHERE `order` > " . intval($order) . " AND `part` = :part";

                $command = Yii::app()->db->createCommand($sql);

                $command->bindParam(":part", $part, PDO::PARAM_STR);

                $command->execute();                    

            }

            $langs = array('ru', 'en');

            $valid = true;

            foreach ($langs as $lang)

                if (isset($_POST[$lang . '_ProjectText']))

                {

                    if (!$project->isNewRecord)

                        $projtext = ProjectText::model()->findByPk($_POST[$lang . '_ProjectText']['id']);

                    if (!$projtext)

                        $projtext = new ProjectText;

                        

                    $projtext->attributes = $_POST[$lang . '_ProjectText'];

                    $valid = $valid && $projtext->validate();

                }


            if($valid && $project->save())

            {

                foreach ($langs as $lang)

                    if (isset($_POST[$lang . '_ProjectText']))

                    {

                        if (!$new)

                            $projtext = ProjectText::model()->findByPk($_POST[$lang . '_ProjectText']['id']);

                        if (!$projtext)

                            $projtext = new ProjectText;

                            

                        $projtext->attributes = $_POST[$lang . '_ProjectText'];

                        $projtext->lang = $lang;

                        $projtext->project_id = $project->project_id;

                        $projtext->save();

                        unset($projtext);

                    }


                $source_dir = dirname(__FILE__) . '/../../../images/source_images';

                $big_dir = dirname(__FILE__) . '/../../../images/big_images';

                $sdir = $source_dir . '/' . $project->project_id;

                $bdir = $big_dir . '/' . $project->project_id;

                

                if ($_POST['imagethumb'])

                {

                    $image = ProjectImage::model()->findByPk($_POST['imagethumb']);

                    if ($image)

                    {

                        Yii::import('application.extensions.Image');                        

                        $thumb = Yii::app()->image->load($source_dir . '/' . $image->image_filename);

                        $thumb->resize(80, 60, ($thumb->width<$thumb->height) ? Image::HEIGHT : Image::WIDTH)->crop(80, 60);


//                        $thumb->resize(80, 60, Image::AUTO);

                        $thumb->save($big_dir . '/../thumb' . $project->project_id . '.jpg', 0666, true);

                    }

                }

                

                if ($_POST['imagedel'])

                {

                    $imagedel = explode(',', $_POST['imagedel']);

                    foreach ($imagedel as $id)

                    {

                        $image = ProjectImage::model()->findByPk($id);

                        if ($image)

                        {

                            if (is_file($source_dir . '/' . $image->image_filename))

                                unlink($source_dir . '/' . $image->image_filename);

                            if (is_file($big_dir . '/' . $image->image_filename))

                                unlink($big_dir . '/' . $image->image_filename);

                            $image->delete();                           

                        }

                    }

                }

                if ($_POST['imageorder'])

                    foreach ($_POST['imageorder'] as $i=>$id)

                    {

                        $sql = "UPDATE `" . ProjectImage::tableName() . "` SET `order` = " . (intval($i)+1) . " WHERE id = " . intval($id);

                        $command = Yii::app()->db->createCommand($sql);

                        $command->execute();

                    }

                    

                if (isset($_POST['ProjectImage']))

                {

                    $allow_types = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);

                    

                    foreach ($_POST['ProjectImage'] as $i => $img)

                    {

                        $project_image[$i] = new ProjectImage;

                        if (isset($_POST['ProjectImage'][$i]))

                            $project_image[$i]->image_filename = CUploadedFile::getInstance($project_image[$i], "add_image[$i]");

                            $image_info = getimagesize($project_image[$i]->image_filename->getTempName());

                        if (($project_image[$i]->image_filename->getName() != '') && (in_array($image_info[2], $allow_types)))

                        {

                            $project_image[$i]->save();

                            

                            if (!file_exists($sdir))

                                mkdir($sdir, 0777, true);

                            $project_image[$i]->image_filename->saveAs($sdir . '/' . $project_image[$i]->id . '.' . $project_image[$i]->image_filename->extensionName);

                            $project_image[$i]->image_filename = $project->project_id . '/' . $project_image[$i]->id . '.' . $project_image[$i]->image_filename->extensionName;

    

                            if (!file_exists($bdir))

                                mkdir($bdir, 0777, true);

                            $image = Yii::app()->image->load($source_dir . '/' . $project_image[$i]->image_filename);

                            $image->resize(700, 550);

                            $image->save($big_dir . '/' . $project_image[$i]->image_filename, 0644, true);

                            

                            $image_info = getimagesize($big_dir . '/' . $project_image[$i]->image_filename);

    

                            $project_image[$i]->project_id = $project->project_id;

                            $project_image[$i]->order = count($project->images)+1;

    

                            $project_image[$i]->image_width = $image_info[0];

                            $project_image[$i]->image_height = $image_info[1];

    

                            $project_image[$i]->save();                     

                        }

                    }

                }

                

                //$project->save();


                if ($_POST['return'])

                    if ($new)

                        $this->redirect(array('update', 'id' => $project->project_id));

                    else

                        $this->refresh();

                else

                    $this->redirect(array('admin'));

                

            }

        }

        if (isset($_POST['cancel']))

            $this->redirect(array('admin'));

        

    }



В общих словах стуктура такова. Три таблицы/модели:

  • список проектов

  • тексты на разных (в данном случае на двух) языках для проектов (многое-к-одному)

  • изображения для проектов (многое-к-одному)

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

p.s. За "минус" к посту #5 остался неприятный осадок. После такого в разы уменьшается желание помогать. Я не прошу "плюсов", но и "минуса" я не заслужил.

А я и не требовал. Если внимательно прочесть мой первый пост то я как раз спрашивал … уж простите если не наколнях стоял при этом …

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

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

Скажу лишь что пока обсуждали тут все я потихонечку все решил самостоятельно.

Могу сказать что сделал совсем иначе.

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

$model->save()

а все остальные манипуляции я провожу в моделе.

в методе beforeValidate я заполняю сво-во модели в котором будут содержаться связанные данные, тут же и определяется что эти данным идут на сохранение или на обновление

в методе rules добавляю правило которое проверяет эти связанные данные

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

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

Во-вторых, как по мне приятнее иметь логичный контроллер и использовать на максимум возможности фреймворка.

Ну а в третьих остается только в виде проверять есть ли ошибки валидации и выводить их.

Мне кажется на эту тему стоило бы написать кукбук… а может уже и не стоило бы :)

Всем спасибо.

И все равно было бы круто если б это все делал сам фреймворк :rolleyes:

samdark, +1

2samdark, ты вообще не читал тему ?

Я в самом начале что на этом сайте я нашел кукбук на эту тему но он не раскрыт полностью.

Т.е. не раскрыты вопросы валидации, ничего в кукбуке не сказано о редактировании и т.д. это не кукбук а фуфло, плюс к тому он не лишен недостатков и в начале темы я все их описал… а ты тут пришел и просто тупо увеличел ссылочность на твой сайт :slight_smile: красавец! -1

как и многие кстати кукбуки тут и там … они не "живые".

2rosko, ты упал в моих глазах совсем :(

to spk

Вы хотите чтобы это работало так?




$team=new Team(array(...attributes_here..));

$team->members[]=new Member(array(...attributes_here..));

$team->save();



Это не проблема, пишите соответствующий Behavior, который будет шерстить relations() на предмет объектов с свойством isNewRecord и делать массовое сохранение. Так же может заниматься оптимизацией операции ‘INSERT’, чтобы делать всего 1 запрос. Это довольно практично, но могут быть ситуации, когда требуется дополнительная обработка. Шаблонный код в контроллере, готовый для дополнительных обработок:




$team=new Team;

$valid=true;


if(isset($_POST['Team'],$_POST['Member']) && is_array($_POST['Member']))

{

    $team->attributes=$_POST['Team'];

    $valid=$team->validate();


    foreach($_POST['Member'] as $key=>$member)

    {

        $members[$key]->attributes=$member;

        $valid=$members[$key]->validate() && $valid;

    }


    if($valid)

    {

        //Если нужна транзакция, то тут можно объявить её начало

        $team->save(false);


        foreach($members as $member)

        {

            $member->teamID=$team->id;

            $member->save(false);

        }

        //Если была начата транзакция, то тут объявляем её конец

    }

}



Если это используется таким образом, то в шаблонах сохраняется возможность использовать все CHtml::active*() методы, даже не взирая на то, что $_POST[‘Member’] массив. В том числе сохраняется возможность выводить ошибки валидации всех моделей через CHtml::errorSummary(), либо общую ошибку по состоянию флага $valid.

Спасибо за ответ.

Я почему-то не очень понимаю как таки вот образом сохранится возможность вывода ошибок и для $members ?

Можете "разжувать" Как из поста мемберс становится объектом ?




   foreach($_POST['Member'] as $key=>$member)

    {

        $members[$key]->attributes=$member; // ВОТ ТУТ !

        $valid=$members[$key]->validate() && $valid;

    }



Спасибо.

to spk




$team=new Team;

//Можно создать нужное кол-во объектов members тут.

$valid=true;



В том месте где я указал комментарий, можно создать нужное кол-во объектов members, для того чтобы иметь возможность передать их в виды, что в свою очередь позволяет использовать CHtml::active* методы и пользоваться CHtml::errorSummary() для вывода ошибок и $team и $members. Или есть вариант делать 1 общую ошибку для всех members по состоянию флага $valid.