Запрос удаленных отношений с использованием таблицы отношений - PullRequest
0 голосов
/ 18 января 2019

Я использую Laravel 5.7 и mysql.У меня есть таблица services и таблица allowed_locations, которая показывает, какие услуги доступны в каждом городе.1 услуга может быть доступна в 1 или более городах.Каждый город принадлежит одному штату, и каждый штат принадлежит одной стране.Я хотел бы вернуть услугу, город, штат и страну, но я не уверен, как установить отношения в каждой из моделей.Могу ли я получить город, штат и страну?

Города

id
state_id

Государства

id
country_id
name

Страны

id
name

Услуги

id
name

Разрешено_Locations

city_id (id from cities table)
service_id (id from Services table)

Идентификатор страны, название 1, США

Штаты

id, country_id, name
3, 1, California
4, 1, Washington
5, 1, Oregon

Города

id, state_id, name
1, 3, San Diego  
2, 4, Seattle
3, 5, Portland

Услуги

id, name 
1, Tax Services
2, Legal Services

РазрешеноМестоположение

city_id, service_id
1, 1
2, 1
3, 2

Services.php модель

public function locations () {
    return $this->belongsToMany('App\cities', 'services_by_location', 'service_id','city_id');
}

towns.php модель

public $with = ['state'];

 public function state() {
    return $this->hasOne(states::class, 'id', 'state_id');
}

states.php модель

public $with = ['country'];

public function country() {
    return $this->hasOne(Countries::class, 'id', 'country_id');
}

Страны.php модель

//

AllowedLocations.php модель

//

Контроллер

$data = Services::with(['locations'])->get();
return response()->json($data, 200);

В настоящее время я возвращаю ответ, подобный этому

{
    {
        id: 1,
        name: Tax Services
        locations: 
        {
            {
                city_id: 1,
                city: San Diego,
                state: {
                    name: California
                }
                country: {
                    name: USA,
                }
            },
            {
                city_id: 2,
                city: Seattle,
                state: {
                    name: Washington
                }
                country: {
                    name: USA,
                }
            },
        }
    },
    {
        id: 2,
        name: Legal Services
        locations: 
        {
            {
                city_id: 3,
                city: Portland,
                state: {
                    name: Oregon
                }
                country: {
                    name: USA,
                }
            },
        }   
    }
}

Вместо вложенных state и country Я хотел бы вернуть город, штат и название страны во вложенном массиве местоположений.Это возможно?

{
    {
        id: 1,
        name: Tax Services
        locations: 
        {
            {
                city_id: 1,
                city: San Diego,
                state: California,
                country: USA
            },
            {
                city_id: 2,
                city: Seattle,
                state: Washington,
                country: USA
            },
        }
    },
    {
        id: 2,
        name: Legal Services
        locations: 
        {
            {
                city_id: 3,
                city: Portland,
                state: Oregon,
                country: USA
            },
        }   
    }
}

Ответы [ 2 ]

0 голосов
/ 19 января 2019

Если я правильно вас понимаю, ваши отношения должны выглядеть примерно так:

Service.php

public function cities()
{
    return $this->belongsToMany(City::class);
}

City.php

public function services()
{
    return $this->belongsToMany(Service::class);
}

public function state()
{
    return $this->belongsTo(State::class);
}

Если возможно, я бы придерживался по умолчанию и переименовал сводную таблицу в city_service

State.php

public function cities()
{
    return $this->hasMany('App\City');
}

public function country()
{
    return $this->belongsTo('App\Country');
}

Country.php

public function states()
{
    return $this->hasMany('App\State');
}

Чтобы получить желаемый результат, вам необходимо использовать аксессоры

City.php

public function getState ()
{
    return $this->state->name;
}

public function getCountry ()
{
    $country = Country::findOrFail($this->state->country_id);
    return $country->name;
}

Теперь это немного дорого, но hasManyThrough () не будет работать здесь, потому что вам нужны обратные отношения. Это было предложено , но не принято. Есть пакет , но не уверен, что он стабильный. Кажется заброшенным.

В любом случае, вы сможете запросить это и получить желаемый результат с помощью:

$services = Service::with('cities')->get();
0 голосов
/ 19 января 2019

Это был интересный вызов, поэтому я настроил его на тестовой машине и решил. Несомненно, есть другие способы сделать это, но это работает, и государство и страна будут доступны для использования на верхнем уровне, как вы хотели.

Параметры не будут отображаться в dd() дампе, хотя они будут доступны для прямого доступа на верхнем уровне, а будет , если вы создадите дамп через JSON.

Я думаю, что самый простой способ помочь - это просто установить аксессоры на модели City. Затем, когда метод locations() вызывается из модели Services, все города имеют необходимую информацию.

Модель города:

protected $fillable=[
    'name',
    'state_id',
];

protected $appends = ['state_name', 'country_name'];

public function state() {
    return $this->hasOne(State::class, 'id', 'state_id');
}

public function getStateNameAttribute ()
{
    return $this->state->name;
}
public function getCountryNameAttribute ()
{
    return $this->state->country->name;
}

Обратите внимание на protected $appends = ['state_name', 'country_name'];. Это позволяет отображать эти дополнительные поля при выгрузке в JSON. Если вам просто нужен доступ к дополнительным полям (state_name и country_name), вам не нужна эта строка - вы можете вытащить поля из части массива уровня города сейчас, так как у нас есть аксессоры в городе модель.

В запрос теперь должна быть включена более активная загрузка, чтобы эти средства доступа не пытались получить имя из нулевого значения. (Вы должны добавить нулевую проверку в аксессор). Я предлагаю сразу потянуть все это так:

$data = \App\Service::with(['locations' => function($query){
    $query->with(['state' => function($query){
        $query->with('country');
    }]);
}])->get();

Выполнение цикла для $data теперь даст вам все необходимое на уровне города. E.g.:

foreach($data as $service){
   echo "Service id: ".$service->id.": ";
   echo $service->name."<br/>";

   echo $service->locations->first()->name.", ";
   echo $service->locations->first()->state_name." -- in the country: ";
   echo $service->locations->first()->country_name;

   echo "<br/><br/><br/>";
}

Это успешно дает:

Service id: 1: Tax Services
Annapolis, Maryland -- in the country: USA

Service id: 2: Legal Services
Potomac, Maryland -- in the country: USA
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...