【问题标题】:Querying distant relationships using a relationship table使用关系表查询远距离关系
【发布时间】:2019-06-13 02:38:24
【问题描述】:

我正在使用 Laravel 5.7 和 mysql。我有一个services 表和一个allowed_locations 表,其中包含每个城市可用的服务。 1 个服务可以在 1 个或多个城市提供。每个城市属于一个州,每个州属于一个国家。我想返回服务、城市、州和国家,但我不确定如何在每个模型中设置关系。我可以检索城市、州和国家吗?

城市

id
state_id

国家

id
country_id
name

国家

id
name

服务

id
name

Allowed_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');
}

cities.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');
}

Countries.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,
                }
            },
        }   
    }
}

我想在位置嵌套数组中返回城市、州和国家名称,而不是嵌套的 statecountry。这可能吗?

{
    {
        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
            },
        }   
    }
}

【问题讨论】:

    标签: laravel laravel-5 eloquent


    【解决方案1】:

    这是一个有趣的挑战,所以我在一台测试机器上进行了设置并解决了这个问题。毫无疑问,还有其他方法可以做到这一点,但这是可行的,并且可以根据需要在顶层使用州和国家/地区。

    参数不会显示在dd() 转储中,但它们可在顶层直接访问,如果您通过 JSON 转储,显示。

    我认为最简单的帮助方法是在 City 模型上设置访问器。然后,当从 Services 模型调用 locations() 方法时,所有城市都有您需要的信息。

    城市模型:

    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_namecountry_name),则不需要此行——您现在可以从数组的 City 级别部分中提取字段,因为我们在 City 中有访问器型号。

    查询现在需要包含更多的急切加载,以便这些访问器不会尝试从空值中获取名称。 (您应该在访问器中添加一个空检查)我建议像这样一次将其全部拉入:

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

    $data 上执行 for 循环现在将为您提供城市级别所需的一切。例如:

    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
    

    【讨论】:

      【解决方案2】:

      如果我对您的理解正确,您的关系应该如下所示:

      服务.php

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

      城市.php

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

      如果可能,我会坚持使用 default 并将数据透视表重命名为 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');
      }
      

      为了得到想要的结果,你必须使用accessors

      城市.php

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

      现在这有点贵,但hasManyThrough() 在这里不起作用,因为您需要反向关系。这是proposed,但未被接受。有一个package,但不确定它是否稳定。似乎被遗弃了。

      无论如何,您应该可以通过以下方式查询并获得所需的结果:

      $services = Service::with('cities')->get();
      

      【讨论】:

      • 变异器在这里不起作用。如果他应用了mutators,它不会在他的输出过程中出现在运行时——他试图在输出时使关系更高一些——与保存无关。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-24
      • 2020-02-08
      • 1970-01-01
      • 2020-11-08
      • 2012-09-06
      • 1970-01-01
      相关资源
      最近更新 更多