Laravel Nova-поле выпадающего списка загрузки, основанное на связи с другим выпадающим списком


Laravel Nova-поле выпадающего списка загрузки, основанное на связи с другим выпадающим списком

29.01.2019 05:01:42 Просмотров 140 Источник

У меня есть этот ресурс под названием Distributor

  ID::make()->sortable(),
            Text::make('Name')
                ->creationRules('required'),
            BelongsTo::make('Region')
                ->creationRules('required')
                ->searchable(),
            BelongsTo::make('Country')
                ->creationRules('required')
                ->searchable(),

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

Регион и страна уже связаны в своих моделях, основанных на отношениях belongsToMany.

Есть ли способ сделать так, чтобы поля работали вместе?

У вопроса есть решение - Посмотреть?

Ответы - Laravel Nova-поле выпадающего списка загрузки, основанное на связи с другим выпадающим списком / Laravel Nova - Load dropdown field based on relationship with of another dropdown

Eric Bachhuber

16.01.2020 03:13:51

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

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

Наше пользовательское поле оставалось довольно простым на стороне Vue. Единственная реальная логика, которую обрабатывает Vue, заключается в том, чтобы вытащить страны/государства из нашего API и заполнить их в выпадающие списки. На стороне PHP нам пришлось переопределить две функции в контроллере нашего поля: fillAttributeFromRequest () и resolve (). Смотреть ниже:

CountryState.РНР :

namespace Gamefor\CountryState;

use Laravel\Nova\Fields\Field;

class CountryState extends Field
{
    public $component = 'country-state';

    /**
     * Hydrate the given attribute on the model based on the incoming request.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  string  $requestAttribute
     * @param  object  $model
     * @param  string  $attribute
     * @return void
     */
    protected function fillAttributeFromRequest($request, $requestAttribute, $model, $attribute)
    {
        parent::fillAttributeFromRequest($request, $requestAttribute, $model, $attribute);

        if ($request->exists('state_id')) {
            $model->state_id = $request['state_id'];
        }

        if ($request->exists('country_id')) {
            $model->country_id = $request['country_id'];
        }
    }

    /**
     * Resolve the field's value for display.
     *
     * @param  mixed  $resource
     * @param  string|null  $attribute
     * @return void
     */
    public function resolve($resource, $attribute = null)
    {
        // Model has both country_id and state_id foreign keys
        // In the model, we have
        //
        //  public function country(){
        //      return $this->belongsTo('App\Country', 'country_id', 'id');
        //  }
        //
        //  public function state(){
        //      return $this->belongsTo('App\State', 'state_id', 'id');
        //  }
        $this->value = $resource->country['name'] . ', ' .  $resource->state['name']; 
    }
}

FormField.Ву

<template>
  <default-field :field="field" :errors="errors">
    <template slot="field">
      <select
        name="country"
        ref="menu"
        id="country"
        class="form-control form-select mb-3 w-full"
        v-model="selectedCountryId"
        @change="updateStateDropdown"
      >
        <option
          :key="country.id"
          :value="country.id"
          v-for="country in countries"
        >
          {{ country.name }}
        </option>
      </select>

      <select
        v-if="states.length > 0"
        name="state"
        ref="menu"
        id="state"
        class="form-control form-select mb-3 w-full"
        v-model="selectedStateId"
      >
        <option :value="state.id" :key="state" v-for="state in states">
          {{ state.name }}
        </option>
      </select>
    </template>
  </default-field>
</template>

<script>
import { FormField, HandlesValidationErrors } from "laravel-nova";

export default {
  mixins: [FormField, HandlesValidationErrors],

  props: {
    name: String
  },

  data() {
    return {
      countries: [],
      states: [],
      allStates: [],
      selectedCountryId: null,
      selectedStateId: null
    };
  },

  created: function() {
    this.fetchCountriesWithStates();
  },

  methods: {
    updateStateDropdown() {
      this.states = this.allStates.filter(
        item => item.country_id === this.selectedCountryId
      );

      this.selectedStateId = this.states.length > 0 ? this.states[0].id : null;
    },

    async fetchCountriesWithStates() {
      const countryResponse = await Nova.request().get("/api/v1/countries");
      const stateResponse = await Nova.request().get("/api/v1/states");

      this.countries = countryResponse.data;
      this.allStates = stateResponse.data;
      this.updateStateDropdown();
    },

    fill(formData){
       formData.append('country_id', this.selectedCountryId);
       formData.append('state_id', this.selectedStateId);
    },
  },
};
</script>

Индексное поле.Ву

<template>
    <span>{{ field.value }}</span>
</template>

<script>
export default {
    props: ['resourceName', 'field',],
}
</script>

Наконец, в массиве полей нашего ресурса Nova:

CountryState::make('Country and State')->rules('required')

Эти образцы определенно нуждаются в настройке, прежде чем они будут "готовы к производству", но, надеюсь, они помогут любому, кто осмелится рискнуть в дикую кроличью нору, которая является настройкой Nova.

Birdperson

04.02.2020 07:57:20

Ответ Эрика очень помог мне. Спасибо!

Но вместо того, чтобы писать свои собственные функции заполнения и разрешения в пользовательском поле Nova, я просто унаследовал поле BelongsTo:

<?php

namespace Travelguide\DestinationSelect;

use App\Models\Destination;
use Laravel\Nova\Fields\Field;
use Laravel\Nova\Http\Requests\NovaRequest;

class DestinationSelect extends \Laravel\Nova\Fields\BelongsTo
{
  /**
   * The field's component.
   *
   * @var string
   */
  public $component = 'destination-select';

  /**
   * Prepare the field for JSON serialization.
   *
   * @return array
   */
  public function jsonSerialize()
  {
    $parentId = null;
    $parentName = null;

    if (isset($this->belongsToId)) {
      $destination = Destination::where('id', $this->belongsToId)->first();
      if (isset($destination) && isset($destination->parent)) {
        $parentId = $destination->parent->id;
        $parentName = $destination->parent->name;
      }
    }

    return array_merge([
      'parent_id' => $parentId,
      'parent_name' => $parentName,
    ], parent::jsonSerialize());
  }
}

Дополнительные данные в функцию jsonSerialize затем может использоваться, чтобы предварительно заполнить свой интерфейс выбора элемента:

<template>
  <default-field :field="field" :errors="errors">
    <template slot="field">
      <select
        name="country"
        ref="menu"
        id="country"
        class="form-control form-select mb-3 w-full"
        v-model="selectedCountryId"
        @change="onCountryChange"
      >
        <option
          :key="country.id"
          :value="country.id"
          v-for="country in countries"
        >
          {{ country.name }}
        </option>
      </select>

      <select
        v-if="regions.length > 0"
        name="region"
        ref="menu"
        id="region"
        class="form-control form-select mb-3 w-full"
        v-model="selectedRegionId"
      >
        <option :value="region.id" :key="region" v-for="region in regions">
          {{ region.name }}
        </option>
      </select>
    </template>
  </default-field>
</template>

<script>
import { FormField, HandlesValidationErrors } from "laravel-nova";

export default {
  mixins: [FormField, HandlesValidationErrors],

  props: ['resourceName', 'field'],

  data() {
    return {
      countries: [],
      regions: [],
      selectedCountryId: null,
      selectedRegionId: null
    };
  },

  created: function() {
    this.fetchCountries();
  },

  methods: {

    async fetchCountries() {
      const countryResponse = await Nova.request().get("/api/destinations");
      this.countries = countryResponse.data;

      // Add 'null' option to countries
      this.countries.unshift({
        name: '-',
        id: null
      });

      if (this.field.parent_id) {
        this.selectedCountryId = this.field.parent_id;
        this.selectedRegionId = this.field.belongsToId || null;
      } else {
        this.selectedCountryId = this.field.belongsToId || null;
      }

      this.updateRegionDropdown();
    },

    async updateRegionDropdown() {
      if (!this.selectedCountryId) {
        return;
      }

      // Get all regions of the selected country
      const regionResponse = await Nova.request().get("/api/destinations/" + this.selectedCountryId);
      this.regions = regionResponse.data;

      // Add 'null' option to regions
      this.regions.unshift({
        name: '-',
        id: null
      });
    },

    onCountryChange() {
      // De-select current region and load all regions of new country
      this.selectedRegionId = null;
      this.updateRegionDropdown();
    },

    fill(formData) {
      if (this.selectedRegionId) {
        formData.append('destination', this.selectedRegionId);
      } else if (this.selectedCountryId) {
        formData.append('destination', this.selectedCountryId);
      }
    },
  },
};
</script>
Помочь в развитии проекта:
Закрыть X