Можно ли выполнить объединение для переменной, созданной подзапросом? - PullRequest
0 голосов
/ 29 июня 2018

Сейчас я выполняю подзапрос, чтобы получить самый последний статус для сервера, этот подзапрос возвращается через переменную last_status.

 //This is ran when WithLastStatusDate() is called
$query->addSubSelect('last_status', ServerStatus::select('status_id')
    ->whereRaw('server_id = servers.id')
    ->latest()
);

$servers = Server::WithLastStatusDate()
    ->OrderBy('servers.id', 'desc')
    ->where('servers.isPublic', '=', 1)
    ->get(); 

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

$servers = Server::WithLastStatusDate()
    ->OrderBy('servers.id', 'desc')
    ->where('servers.isPublic', '=', 1)
    ->leftjoin('statuses','servers.last_status', '=', 'statuses.id')
    ->get(); 

Кто-нибудь может указать мне правильное направление, как это сделать?

РЕДАКТИРОВАТЬ ::

Таблица серверов:

 Schema::create('servers', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->string('url');
            $table->boolean('isPublic');
            $table->timestamps();
        });

Таблица серверных состояний:

Schema::create('server_statuses', function (Blueprint $table) {
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->integer('server_id')->unsigned();
            $table->foreign('server_id')->references('id')->on('servers')->onDelete('cascade');
            $table->integer('status_id')->unsigned();
            $table->foreign('status_id')->references('id')->on('statuses');
            $table->timestamps();
        });

таблица статусов:

Schema::create('statuses', function (Blueprint $table) {
    $table->engine = 'InnoDB';
    $table->increments('id');
    $table->string('key');
    $table->string('status');
    $table->timestamps();
});

Как $ серверы выглядят после подзапроса:

enter image description here

Необработанный SQL запроса:

select `servers`.*, (select `status_id` from `server_statuses` where server_id = servers.id order by `created_at` desc limit 1) as `last_status` from `servers` where `servers`.`isPublic` = '1' order by `servers`.`id` desc

РЕДАКТИРОВАТЬ 2 ::

    $servers = DB::table('servers as sv')
        ->join('server_statuses as ss', 'sv.id', '=', 'ss.server_id')
        ->join('statuses as st', 'ss.status_id', '=', 'st.id')
        ->WithLastStatus()
        ->OrderBy('servers.id', 'desc')
        ->where('servers.isPublic', '=', 1)
        ->get();

Ответы [ 3 ]

0 голосов
/ 29 июня 2018

Насколько я понимаю, обе ваши модели Server и Status имеют отношение OneToMany к ServerStatus. В этом случае вы можете подделать отношение OneToOne для вашей модели Server, которая выбрана в качестве последней строки serverStatuses():

class Server
{
    public function serverStatuses()
    {
        return $this->hasMany(ServerStatus::class, 'server_id', 'id');
    }

    public function latestServerStatus()
    {
        return $this->hasOne(ServerStatus::class, 'server_id', 'id')
            ->latest(); // this is the most important line of this example
                        // `->orderBy('created_at', 'desc')` would do the same
    }
}

class ServerStatus
{
    public function server()
    {
        return $this->belongsTo(Server::class, 'server_id', 'id');
    }

    public function status()
    {
        return $this->belongsTo(Status::class, 'status_id', 'id');
    }
}

class Status
{
    public function serverStatuses()
    {
        return $this->hasMany(ServerStatus::class, 'status_id', 'id');
    }
}

Затем вы также можете загрузить последнюю версию статуса для ваших серверов и сам статус:

Server::with('latestServerStatus.status')->get();

Обратите внимание, что $server->latestServerStatus - это не коллекция, а один объект, как в обычном OneToOne отношении.

0 голосов
/ 29 июня 2018

Объединение левых соединений с предложением WHERE подзапроса:

$servers = Server::select('servers.*', 'statuses.status as status_name')
    ->leftJoin('server_statuses', function($join) {
        $join->on('server_statuses.server_id', '=', 'servers.id')
            ->where('server_statuses.id', function($query) {
                $query->select('id')
                    ->from('server_statuses')
                    ->whereColumn('server_id', 'servers.id')
                    ->latest()
                    ->limit(1);
            });
    })
    ->leftJoin('statuses', 'statuses.id', '=', 'server_statuses.status_id')
    ->where('servers.isPublic', '=', 1)
    ->orderBy('servers.id', 'desc')
    ->get();
0 голосов
/ 29 июня 2018

Поскольку я не уверен, что именно вы хотите получить от вашего запроса, я выберу длинное решение и добавлю несколько примеров. С этими таблицами у вас должны быть следующие модели: Модель сервера:

class Server extends Model {
    public function statuses() {
        return $this->belongsToMany(Status::class, 'server_statuses');
    }
}

Модель состояния:

class Status extends Model {
    public function servers() {
        return $this->belongsToMany(Server::class, 'server_statuses');
    }
}

Примеры: Получить последний статус сервера:

Server::find($serverId)->statuses()->latest()->first()->status;

Получить все статусы сервера:

Server::find($serverId)->statuses;

Получить конкретный статус сервера:

Server::find($serverId)->statuses()->where('status', 'SomeStatus')->get();

Получить серверы с определенным статусом:

Server::whereHas('statuses', function ($join) use ($status) {
    return $join->where('status', $status);
})->get();

Надеюсь, вы найдете свой ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...