Получить все элементы, которые не существуют в другой таблице

#php #sql #laravel #eloquent

#php #sql #laravel #красноречивый

Вопрос:

Я использую Laravel Framework 6.16.0 и у меня есть две таблицы, logos и companies :

Логотипы:

         Schema::create('logos', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('companies_id')->default('999999');
            $table->string('logo_path')->nullable($value = true); // path to the logo image
            $table->timestamps();
        });
  

Компании:

         Schema::create('companies', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('symbol'); // AAPL
            $table->string('name'); // company name such as Apple Inc.
            $table->timestamps();
        });
  

Модели:

 class Logo extends Model
{
    protected $table = 'logos';

    protected $guarded = ['id'];

    public function company()
    {
        return $this->belongsTo(Company::class, 'companies_id');
    }
}
  
 class Company extends Model
{
    protected $table = 'companies';

    protected $guarded = ['id'];
}

  

Я хочу получить все символы, у которых нет логотипа в logo-table , а логотип для 90 days не обновлялся.

Я попробовал следующее:

         $symbolsWithoutLogos = DB::table('companies')
            >leftJoin('logos', 'companies.id', '=', 'logos.companies_id')
            ->whereDate('logos.updated_at', Carbon::now()->subDays(90))
            ->whereNull('companies.logo_image')
            ->orderBy('name', 'ASC')
            ->get('companies.*');
  

Результатом этого запроса является:

 select `companies`.* from `companies` left join `logos` on `companies`.`id` = `logos`.`companies_id` where date(`logos`.`updated_at`) = ? order by `name` asc
  

Однако я не получаю результата при выполнении этого запроса, даже если в моей таблице companies достаточно записей:

введите описание изображения здесь

Есть предложения, что я делаю неправильно?

Я ценю ваши ответы!

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

1. Я думаю, вам следует исправить информацию, которую вы нам предоставляете. Потому что, во-первых, я вижу столбец «logo_path», а не столбец «logo_image», как вы пишете ниже в конструкторе. Во-вторых, в необработанном запросе Mysql я не вижу «logo_image» или «logo_path». Наконец, вы говорите о получении «символов», у которых нет логотипа, но которые последний раз обновляли логотип по крайней мере 90 дней назад, это противоречиво.

Ответ №1:

Вы можете достичь этого с помощью Eloquent вместо использования left join и DB facade.

Я предполагаю, что соотношение между Company и Logo равно 1: 1 или 1:n. Если мы предположим, что это 1: 1, то ваш запрос будет выглядеть следующим образом:

 Company::whereHas('logo', function($query) {
    return $query->where('date', '<', Carbon::now()->subDays(90));
})->orWhereDoesntHave('logo')->...
  

Вы должны проверить: Отсутствие связи с запросом

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

Модель логотипа должна быть примерно такой:

 class Logo extends Model
{
    // protected $table = 'logos'; 
    // not necessary, Laravel already knows to use logos table

    protected $guarded = ['id'];

    public function company()
    {
        // return $this->belongsTo(Company::class, 'companies_id');
        // no need to specify foreign key if we follow naming convention
        // foreign key should be {foreign_model_singular}_id
        // in this case it is company_id
        return $this->belongsTo(Company::class);
    }
}
  

Модель Company также не требует указания имени таблицы, она уже знает, что это должны быть компании.

Модель компании:

 class Company extends Model
{
    // protected $table = 'companies';

    protected $guarded = ['id'];

    public function logo()
    {
        return $this->hasOne(Logo::class);
    }
}
  

Пожалуйста, проверьте, как работают свойства $guarded и $fillable. Если только id установлен как защищенный, то все остальные свойства не защищены, не уверен, что это было вашим намерением.

Полный запрос должен быть:

 Company::whereHas('logo', function($query) {
    return $query->where('updated_at', '<', Carbon::now()->subDays(90));
})->orWhereDoesntHave('logo')
->orderBy('name', 'ASC')
->get();
  

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

1. Пожалуйста, посмотрите мой обновленный вопрос с двумя моделями.

Ответ №2:

->whereDate(‘logos.updated_at’), где date сравнивает только часть даты столбца DateTime … попробуйте это с помощью toDateString() из carbon …

   $symbolsWithoutLogos = DB::table('companies')
            ->leftJoin('logos', 'companies.id', '=', 'logos.companies_id')
                ->whereDate('logos.updated_at', Carbon::now()->subDays(90)->toDateString())
                ->whereNull('companies.logo_image')
                ->orderBy('name', 'ASC')->select('companies.*')
                ->get();