Как получить значение self из строки для использования в другом запросе select в модели self eloquent?

#php #mysql #laravel #eloquent

#php #mysql #laravel #eloquent

Вопрос:

Я новичок в laravel, только начал несколько дней назад, и я уже искал об этом, но ничего не нашел по моему вопросу. Например… У меня есть таблица offers и offers_prices.

Предложения:

  ---- ----------- 
| id |   name    |
 ---- ----------- 
|  1 | Product 1 |
|  2 | Product 2 |
|  3 | Product 3 |
|  4 | Product 4 |
 ---- ----------- 
 

offers_prices:

  ---- ------------ ------- --------------------- 
| id |  offer_id  | price |     created_at      |
 ---- ------------ ------- --------------------- 
|  1 |          1 | 65.90 | 2020-12-17 16:00:00 |
|  2 |          1 | 64.99 | 2020-12-17 17:00:00 |
|  3 |          1 | 58.90 | 2020-12-17 18:00:00 |
|  4 |          1 | 60.99 | 2020-12-17 19:00:00 |
 ---- ------------ ------- --------------------- 
 

Чтобы получить текущую цену предложения (60,99) Я создал приведенную ниже функцию, чтобы получить ее из модели relationship eloquent:

Models/Offer.php

 public function price() {
    return parent::hasOne(OfferPrices::class)
        ->where("date", "<=", CarbonCarbon::now()->toDateTimeString())
        ->orderBy("date", "DESC")
        ->limit(1);
}
 

И теперь я хочу получить самую последнюю предыдущую цену, но выше текущей цены, как в приведенном ниже запросе:

 SELECT *
FROM offers_prices
WHERE offers_prices.offer_id = 1
AND
offers_prices.price > (
    SELECT offers_prices.price
    FROM offers_prices
    WHERE offers_prices.offer_id = 1
    AND offers_prices.date <= NOW()
    ORDER BY offers_prices.date DESC
    LIMIT 1
)
AND offers_prices.date <= NOW()
ORDER BY offers_prices.date DESC
LIMIT 1;
 

Это означает, что недавняя предыдущая и более высокая, чем текущая цена, не может быть 58,90, потому что она ниже текущей (60,99). Должно быть 64,99.
Как я могу это сделать? Я попытался создать другую функцию в модели self eloquent:

Models/Offer.php

 public function prev_high_price() {
    return parent::hasOne(OfferPrices::class)
        ->where("date", "<=", CarbonCarbon::now()->toDateTimeString())
        ->where("price", ">", $this->price()->price)
        ->orderBy("date", "DESC")
        ->limit(1);
}
 

Но это не работает..

Ответ №1:

Вы используете hasOne связь для связи с базой данных, которая больше похожа на hasMany . У вас Offers может быть много Prices . Ваше именование таблицы также противоречит соглашению, поскольку вы не используете сводную таблицу, но назвали свою offers_prices таблицу так, как если бы она была.

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

Чтобы ответить на ваш вопрос, учитывая следующие две миграции:

Предлагает перенос таблиц

 class CreateOffersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('offers', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('offers');
    }
}
 

Миграция таблицы цен

 class CreatePricesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('prices', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->foreignId('offer_id');
            $table->integer('price');
            $table->foreign('offer_id')->references('id')->on('offers');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('prices');
    }
}
 

Определите следующее в вашей Offer модели:

 class Offer extends Model
{
    use HasFactory;

    public function prices()
    {
        return $this->hasMany(Price::class);
    }

    public function lastPrice()
    {
        return $this->prices()->orderBy('id', 'desc')->limit(1);
    }

    public function lastHighPrice()
    {
        return $this->prices()
            ->where('id', '<', $this->lastPrice()->first()->id)
            ->orderBy('price', 'desc')
            ->limit(1);
    }
}
 

Запустите средства миграции и обработки базы данных (если они у вас есть, в противном случае просто добавьте данные вручную). В вашем терминале, пока вы находитесь в текущем рабочем каталоге, находится ваш проект Laravel, запустите tinker :

 php artisan tinker
 

Теперь внутри tinker вы можете играть со своими данными и моделями.

 // get a collection of all prices for the Offer with id 1
Offer::find(1)->prices

// get a collection with the last price for the offer with id 1
Offer::find(1)->lastPrice

// get a collection with the previous highest price for the offer with id 1
Offer::find(1)->lastHighPrice
 

Комментарии:

1. Это работает, когда «find (id)», но не работает, когда я пытаюсь получить его от UserOffersFavorited в пользовательской модели: См. prnt.sc/w53ben Но когда он запускается $this->price()->first()->price , он не получает цену из идентификатора иностранного предложения. Когда я отслеживаю переменную выше, она просто возвращает первую цену из всей таблицы, независимой от offer_id (чего не должно произойти)

2. @CarlosLoureiro Ну да, нигде в вашем первоначальном вопросе вы не ссылались на User UserOffersFavorited таблицу модели или базы данных, и ни один из примеров таблицы не содержит ссылки на a User . Это звучит как совершенно другой вопрос.

3. Да. Но вы знаете, как я могу заставить его работать так, как мне нужно?

4. @CarlosLoureiro Не зная больше о вашем варианте использования, например, как вы определяете UserOffersFavorited. Однако это вопрос, отличный от того, который вы задали, и поэтому должен быть новый поток. На заданный вами вопрос был дан ответ.