【问题标题】:How addSelect() Builder method works in LaraveladdSelect() Builder 方法在 Laravel 中的工作原理
【发布时间】:2016-03-31 23:43:06
【问题描述】:

假设我有这些模型:

用户模型

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'User';

    protected $fillable =
    [
       'username', 'favoriteColor'
    ];          

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

飞行模型

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'Flight';

    protected $fillable =
    [
       'flightName', 
    ];              
}   

每当我这样做时,我都会尝试通过急切加载来做一些事情:

$usersWithFlights = User::with(['flights'])->get();

我得到这样的数据:(如果没问题,这就是我所期望的)

{
    "userID": 1,
    "favoriteColor": green
    "flights": 
    [
       {
           "flightID": 2240,
           "flightName" : "To Beijing fellas"
       },
       {
           "flightID": 4432,
           "flightName" : "To Russia fellas"
       },       
    ]
}

然后我想使用这样的 select raw 添加一列:(不要想1 as number 是多么愚蠢,这只是为了示例。

$usersWithFlights = User::with(['flights'])->addSelect(DB::raw('1 as number'))->get();

我得到这样的数据:

[
    {
        "userID": 1,
        "favoriteColor": green
        "flights": []
    }
]

问题

addSelect() 方法是针对这种行为而设计的吗?如果不是,是否有其他解决方法来实现这一目标?

注意

我知道我可以在 select 方法中添加类似 Flights.*, Users.* 的内容,但我想知道 addSelect 方法是否可以这样工作

【问题讨论】:

    标签: php laravel laravel-5 eager-loading


    【解决方案1】:

    如果你需要 select 的默认值并添加一些额外的值,你可以使用这个构造:

    $usersWithFlights = User::with(['flights'])->select()->addSelect(DB::raw('1 as number'))->get();
    

    【讨论】:

      【解决方案2】:

      首先看一下addSelect方法:

      public function addSelect($column)
      {
          $column = is_array($column) ? $column : func_get_args();
          $this->columns = array_merge((array) $this->columns, $column);
          return $this;
      }
      

      这只是通过与现有的选定列合并来添加列。所以,当你使用这样的东西时:

      $usersWithFlights = User::with(['flights'])->addSelect(DB::raw('1 as number'))->get();
      

      查询变为select 1 as number from flights,并且由于您的关系取决于父查询中的列值,因此如果没有任何实际列值,则不会检索相关记录。要检查查询,您可以尝试以下操作:

      \DB::enableQueryLog();
      $usersWithFlights = User::with(['flights'])->addSelect(DB::raw('1 as number'))->get();
      dd(\DB::getQueryLog());
      

      另外,如果您在添加选择中使用真实的列名,并且如果您不选择相关列,例如,如果您使用addSelect('someString'),那么您将不会获得任何相关记录,因为您还必须选择建立关系的相关列,在这种情况下,您可以尝试以下操作:

      $usersWithFlights = User::with(['flights'])->addSelect(['id', 'someString']))->get();
      

      测试:

      这是DB::getQueryLog() 的测试结果,我在DB 中使用了类似但不同的表,即:

      Post->with('comments')->addSelect(DB::raw('1 as number'))->get();
      dd(DB::getQueryLog());
      

      结果:

      0 => array:3 [▼
        "query" => "select 1 as number from "posts""
        "bindings" => []
        "time" => 9.0
        ]
      1 => array:3 [▼
        "query" => "select * from "comments" where "comments"."post_id" in (?)"
        "bindings" => array:1 [▼
          0 => null
        ]
        "time" => 1.0
      ]
      

      更新:

      addSelect 方法在您手头有一个带有一些选定字段的查询对象并且稍后您想在选择列表中添加更多字段时很有用,因此示例可能如下所示:

      $userWithFlights = User::with('flights')->select(['id', 'someColumn']);
      // Some code ... 
      if(SomeCondition) {
          // So, now the query will be something like this:
          // ->select(['id', 'someColumn', 'anotherField'])
          $userWithFlights->addSelect('anotherField');
      }
      
      $result =  $userWithFlights->get();
      

      如果您想指定要选择的某些字段,则可以改用select($fieldsArray)。你也可以试试这样的:

      $userWithFlights = User::with('flights')->get(['id', 'someColumn']);
      

      你也可以试试这样的:

      $userWithFlights = User::with(['flights' => function($query) {
          $query->select('user_id', 'flight_title');
      }])->get(['id', 'someColumn']);
      

      【讨论】:

      • 感谢您提供如此详尽的答案。现在我得到了很多东西。但是我该如何解决我的问题呢?我是否必须调用 select 方法键入所有字段?
      • 其实你可以完全避免addSelect,当你有一个带有选定字段的单独查询对象,然后你想添加更多要选择的字段时,这很有用。
      • select() 方法可以做到这一点。我想这就是我的方式
      • 是的,您可以使用select('fieldName') 指定字段,但添加选择会将新字段与预选字段合并。此外,使用 select() 时,必须包含建立关系的主键,否则将不会加载相关记录。另外,您可以使用Flight::with('someRelation')->get(['id', 'someColumn'])
      • 在 Laravel 6 上,我发现 addSelect() 正在用选择项中的单列替换整个选择列表,而不是添加到它们中。我正在使用$hidden 隐藏一些非常大的列,所以这可能会影响它。但只是指出这种方法可能不如预期的那么健壮。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多