Laravel редактирует неглубокий вложенный ресурс

Я использую следующий ресурс, используя неглубокое вложение

Route::resource('organizations.emaildomains', 'OrganizationEmailDomainController', ['except' => ['show']])->shallow();

Получил следующую таблицу с двумя записями, которая является результатом index.blade.php

введите здесь описание изображения

Допустим, мы хотим изменить tiagoperes.eu на stackoverflow.com. Я бы пошел в режим редактирования

введите здесь описание изображения

измените соответствующее поле и нажмите кнопку «Сохранить». Это результат

введите здесь описание изображения

Как видите, запись не обновляется.

Проверка $request->all() в update() контроллера

введите здесь описание изображения

и данные формы на вкладке сети

введите здесь описание изображения

и данные отправляются на

http://localhost/app/public/emaildomains/7

который соответствует URI в неглубокой вложенности.


В edit.blade.php у меня есть следующая форма для обработки обновлений

<form method="post" action="{{ route('emaildomains.update', ['emaildomain' => $email_domain->id]) }}" autocomplete="off">
    @csrf
    @method('put')

    <h6 class="heading-small text-muted mb-4">{{ __('Email Domain information') }}</h6>
    <div class="pl-lg-4">
        <div class="form-group{{ $errors->has('organization_id') ? ' has-danger' : '' }}">
            <label class="form-control-label" for="input-organization_id">{{ __('Organization') }}</label>
            <select name="organization_id" id="input-organization" class="form-control{{ $errors->has('organization_id') ? ' is-invalid' : '' }}" placeholder="{{ __('Organization') }}" required>
                @foreach ($organizations as $organization)
                    <option value="{{ $organization->id }}" {{ $organization->id == old('organization_id', $email_domain->organization->id) ? 'selected' : '' }}>{{ $organization->name }}</option>
                @endforeach
            </select>
            @include('alerts.feedback', ['field' => 'organization_id'])
        </div>

        <div class="form-group{{ $errors->has('email_domain') ? ' has-danger' : '' }}">
            <label class="form-control-label" for="input-email_domain">{{ __('Email Domain') }}</label>
            <input type="text" name="email_domain" id="input-email_domain" class="form-control{{ $errors->has('email_domain') ? ' is-invalid' : '' }}" placeholder="{{ __('Email Domain') }}" value="{{ old('email_domain', $email_domain->email_domain) }}" required autofocus>

            @include('alerts.feedback', ['field' => 'email_domain'])
        </div>

        <div class="text-center">
            <button type="submit" class="btn btn-success mt-4">{{ __('Save') }}</button>
        </div>
    </div>
</form>

а вот методы редактирования и обновления в OrganizationEmailDomainController

/**
 * Show the form for editing the specified resource.
 *
 * @param  \App\OrganizationEmailDomain  $email_domain
 * @return \Illuminate\Http\Response
 */
public function edit(Request $request, OrganizationEmailDomain $email_domain, Organization $model)
{
    $path = $request->path();

    $id = (int)explode('/', $path)[1];

    $emailDomain = OrganizationEmailDomain::find($id);

    return view('organizations.emaildomains.edit', ['email_domain' => $emailDomain->load('organization'), 'organizations' => $model::where('id', $emailDomain->organization_id)->get(['id', 'name'])]);        

}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \App\OrganizationEmailDomain  $email_domain
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, OrganizationEmailDomain $email_domain)
{

    $email_domain->update($request->all());

    $organization_id = (int)$request->all()['organization_id'];

    return redirect()->route('organizations.emaildomains.index', ['organization' => $organization_id])->withStatus(__("Org's email domain successfully updated."));
}

и это модель (обратите внимание, я использую таблицу с именем, отличным от ожидаемого по умолчанию - protected $table = 'email_domains';)

class OrganizationEmailDomain extends Model
{
    protected $fillable = [
        'email_domain', 'organization_id'
    ];

    protected $table = 'email_domains';

    /**
     * Get the organization
     *
     * @return \Organization
     */
    public function organization()
    {
        return $this->belongsTo(Organization::class);
    }

}

person Tiago Martins Peres 李大仁    schedule 03.03.2021    source источник


Ответы (1)


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

Laravel автоматически разрешает модели Eloquent, определенные в маршрутах или действиях контроллера, чьи имена переменных с указанием типа совпадают с именем сегмента маршрута. Например:

use App\OrganizationEmailDomain;

Route::put('emaildomains/{emailDomain}', [OrganizationEmailDomainController::class, 'update']);

Затем в вашем контроллере должно быть следующее

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \App\OrganizationEmailDomain  $emailDomain
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, OrganizationEmailDomain $emailDomain)
{

    $emailDomain->update($request->all());

    return redirect()->route('organizations.emaildomains.index', [
            'organization' => $request->organization_id
    ])->withStatus(__("Org's email domain successfully updated."));
}

Обратите внимание, что если имя переменной $emailDomain отличается от сегмента маршрута {emailDomain}, то laravel не сможет разрешить модель. Поэтому вы получите пустую модель OrganizationEmailDomain, и она не будет обновлять никаких данных. Поэтому обязательно укажите то же имя, что и в маршруте.

Чтобы проверить правильность названия маршрута, выполните команду php artisan route:list, и вы увидите маршрут и название сегмента.


Редактировать

Чтобы решить проблему, я побежал

php artisan route:list

который показал

organizations/{organization}/emaildomains/{emaildomain} | organizations.emaildomains.update

Итак, изменив OrganizationEmailDomainController.php на

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\OrganizationEmailDomainRequest  $request
 * @param  \App\OrganizationEmailDomain  $emaildomain
 * @return \Illuminate\Http\Response
 */
public function update(OrganizationEmailDomainRequest $request, OrganizationEmailDomain $emaildomain)
{

    $emaildomain->update($request->all());

    return redirect()->route('organizations.emaildomains.index', ['organization' => $request->organization_id])->withStatus(__("Org's email domain successfully updated."));
}

было достаточно.

введите здесь описание изображения

Обратите внимание, что единственное необходимое изменение было в контроллере с $email_domain на $emaildomain, но также был удален ненужный бит для получения id организации и вместо него использовался предложенный выше через $request.

person MrEduar    schedule 05.03.2021
comment
Спасибо, мистер Эдуар. Будет давать щедрость всякий раз, когда это возможно. - person Tiago Martins Peres 李大仁; 05.03.2021