CakePHP3 BelongsToMany с множественным выбором и дополнительными данными для сохранения в сводной таблице - PullRequest
0 голосов
/ 08 января 2019

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

Например, когда я пытаюсь отредактировать свой профиль, у меня есть два конкретных друга, которые являются своего рода тегами. Пользователь может ввести свои желаемые навыки и свои настоящие навыки. Для автозаполнения каждого поля навыка я использую другую таблицу, называемую навыками. И чтобы связать их вместе, я использую третью таблицу под названием skill_map. Два поля навыков можно найти в форме профиля и форме курса. Я хотел бы использовать одну и ту же сводную таблицу для контекста курсов и профилей.

Вот мое представление таблиц

1- таблица навыков_карты (сводная таблица) skill_id | INT (11) content_type | varchar (255) (используется для «полиморфизма», пример контекста: Profiles.desiredSkills или Courses.desiredSkills - я использую модель для имени, а затем тип навыков) content_type_id | INT (11) создано | Дата и время модифицированный | Дата и время

2- таблица профилей ACCOUNT_TYPE | INT (11) first_name | VARCHAR (255) last_name | VARCHAR (255) пол | VARCHAR (255) пробкового | VARCHAR (255) активный | INT (11) создано | Дата и время модифицирована | Дата и время

3- Таблица умений (используется для автозаполнения) активный | INT (1) имя | VARCHAR (255) слизняк | VARCHAR (255) создано | Дата и время модифицированный | Дата и время

4- стол для курсов название | VARCHAR (255) слизняк | VARCHAR (255) описание | Текст

Мой профиль В таблице содержатся мои отношения, я добавил условия в мою принадлежность ToMany, которую вы можете использовать позже с моими запросами «найти с содержимым».

public function initialize(array $config){
    parent::initialize($config);

    $this->setTable('profiles');
    $this->setDisplayField('id');
    $this->setPrimaryKey('id');

    $this->addBehavior('Timestamp');

    $this->hasOne('Users')->setForeignKey('profile_id');

    $this->belongsToMany('Businesses', [
        'foreignKey' => 'profile_id',
        'targetForeignKey' => 'business_id',
        'joinTable' => 'businesses_profiles'
    ]);

    $this->belongsToMany('Schedules', [
        'foreignKey' => 'profile_id',
        'targetForeignKey' => 'schedule_id',
        'joinTable' => 'schedules_profiles'
    ]);

    $this->belongsToMany('ActualSkills', [
        'className' => 'Skills',
        'foreignKey' => 'content_type_id',
        'targetForeignKey' => 'skill_id',
        'joinTable' => 'skills_map',
        'conditions' => ['SkillsMap.content_type' => 'Profiles.ActualSkills'],
    ]);

    $this->belongsToMany('DesiredSkills', [
        'className' => 'Skills',
        'foreignKey' => 'content_type_id',
        'targetForeignKey' => 'skill_id',
        'joinTable' => 'skills_map',
        'conditions' => ['SkillsMap.content_type' => 'Profiles.DesiredSkills'],
    ]);

}

Мои НавыкиТаблица отношений

public function initialize(array $config) {
    parent::initialize($config);

    $this->setTable('skills');
    $this->setDisplayField('name');
    $this->setPrimaryKey('id');

    $this->addBehavior('Timestamp');

    $this->belongsToMany('Profiles', [
        'foreignKey' => 'skill_id',
        'targetForeignKey' => 'content_type_id',
        'joinTable' => 'skills_map',
        'through' => 'SkillsMap'
    ]);

}

Таблица моих навыков

public function initialize(array $config){
    parent::initialize($config);

    $this->setTable('skills_map');
    $this->setDisplayField('id');
    $this->setPrimaryKey('id');

    $this->addBehavior('Timestamp');

    $this->belongsTo('Skills');
    $this->belongsTo('Profiles');

}

Форма моего профиля (форма курса будет похожа на теги)

 <div class="card card-profile-skills py-4 mt-4">
    <div class="card-body">
        <fieldset>
            <div class="form-row">
                <div class="col">
                    <div class="form-group">
                        <label for="description"><?= __('edit_profile_actual_skills_label') ?></label>
                        <?=
                        $this->Form->input('actual_skills._ids', [
                            'label' => false,
                            'multiple' => 'multiple',
                            'type' => 'select',
                            'data-js-multiple-skills-select' => '',
                            'data-ajax-url' => $this->Url->build(['_name' => 'get_ajax_skills_list_path'], true),
                            'empty' => 'placeholder',
                            'options' => $selectedActualSkills,
                            'default' => array_keys($selectedActualSkills),
                        ]);
                        ?>
                    </div>
                </div>
            </div>
           <div class="form-row mt-4">
                <div class="col">
                    <div class="form-group">
                        <label for="description"><?= __('edit_profile_desired_skills_label') ?></label>
                        <?=
                        $this->Form->input('desired_skills._ids', [
                            'label' => false,
                            'multiple' => 'multiple',
                            'type' => 'select',
                            'data-js-multiple-skills-select' => '',
                            'data-ajax-url' => $this->Url->build(['_name' => 'get_ajax_skills_list_path'], true),
                            'empty' => 'placeholder',
                            'options' => $selectedDesiredSkills,
                            'default' => array_keys($selectedDesiredSkills),
                        ]);
                        ?>
                    </div>
                </div>
            </div>
        </fieldset>
    </div>

И мой контроллер

public function edit($hash = null){
$profile = $this->Profiles->findByHash($hash)->contain([
    'ActualSkills' => function ($q) {
        return $q;
        //return $q->where(['Profiles.is_published' => true]); /* @ todo: finish request to fit with context skills */
    },
    'DesiredSkills' => function ($q) {
        return $q;
        //return $q->where(['Profiles.is_published' => true]); /* @ todo: finish request to fit with context skills */
    }
])->formatResults(function($results){
    return $results->map(function($row){
        $row->data = json_decode($row->data);
        return $row;
    });
})->first();

$redirect = ['_name' => 'home'];

$associated = [
    'associated' => [
        'ActualSkills',
        'ActualSkills._joinData',
        'DesiredSkills',
        'DesiredSkills._joinData',
    ]
];

if (!$profile || !$profile->isProfileOwner($this->_currentUser)) {
    $this->Flash->error(__('Retourner une 404'));
}

$selectedActualSkills = $this->Util->generateList($profile->actual_skills, ['key' => 'id', 'value' => 'name']);
$selectedDesiredSkills = $this->Util->generateList($profile->desired_skills, ['key' => 'id', 'value' => 'name']);

if ($this->request->is(['patch', 'post', 'put'])) {

    $profile = $this->Profiles->patchEntity($profile, $this->request->getData(), $associated);
    $profile->set('slug');

    if ($this->Profiles->save($profile, $associated)) {

        /* <BEGIN> Update Pivot table */

        if($profile->actual_skills)
        {
            foreach($profile->actual_skills as $skill)
            {
                $pivotRow = $this->Profiles->SkillsMap->findBySkillId($skill->id)->where([
                    'content_type_id' => $profile->id
                ])->first();

                $pivotRow->content_type = $this->name . '.ActualSkills' ;

                $this->Profiles->SkillsMap->save($pivotRow);
            }
        }

        /* <END> Update Pivot table */

        if (isset($profile->save_btn)) {
            $redirect = ['_name' => 'my_profile_path', 'hash' => $profile->hash];
        }

        $this->Flash->success(__('edit_profile_success'));

        return $this->redirect($redirect);

    }


    $this->Flash->error(__('edit_profile_error'));
}


$this->set(compact('profile', 'selectedActualSkills', 'selectedDesiredSkills'));

}

Итак, мой столбец content_type должен быть заполнен при сохранении. Я хотел бы использовать _joinData, но я прочитал в книге Cake, которая не будет работать. Я говорю перед тем, как помещать условия в ссылки на таблицы (content_type для описания контекста, из профиля или формы курса), для запросов и, например, для извлечения выбранных опций моих навыков. Но если мои данные не сохраняются при редактировании формы, мой запрос не может работать. Я не хочу создавать три таблицы навыков, возможно ли достичь этого только одной таблицей?

Большое спасибо за вашу помощь! ; -)

Laurent.

...