Skip to content

Different values from counting #49420

Closed
Closed
@k0mar12

Description

@k0mar12

Laravel Version

v10.5.0

PHP Version

8.2.4

Database Driver & Version

MySQL 8.0.29

Description

I have next models:

Round

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Round extends Model
{
    protected $fillable = ['phase', 'type', 'lobby_mode', 'composition', 'index'];

    public function sides(): HasMany
    {
        return $this->hasMany(Side::class);
    }

    public function mainSides(): HasMany
    {
        return $this->sides()->mainTypes();
    }

    public function filledMainSides(): HasMany
    {
        return $this->mainSides()->filled();
    }
}

Side

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Contracts\Database\Query\Builder;

class Side extends Model
{
    protected $fillable = ['type'];

    public function round(): BelongsTo
    {
        return $this->belongsTo(Round::class);
    }

    public function participants(): MorphMany
    {
        return $this->morphMany(Participant::class, 'participial');
    }

    public function scopeFilled(Builder $builder): Builder
    {
        return $builder
            ->withCount('participants as joined_members')
            ->withAggregate('round', 'composition')
            ->havingRaw('`joined_members` = `round_composition`');
    }

    public function scopeMainTypes(Builder $builder): Builder
    {
        return $builder->where('type', 0)->orWhere('type', 1);
    }
}

Given
In total round has 2 main sides, Dire and Radiant, but filled only one. Filled side it is side which has a completed list of users.

Case 1

Description

I try to fetch Round with filled sides - i have correct result.

Dump

Screenshot 2023-12-18 at 19 33 57

SQL

select * from `rounds` where `id` = 109 limit 1
select
  `sides`.*,
  (
    select
      count(*)
    from
      `participants`
    where
      `sides`.`id` = `participants`.`participial_id`
      and `participants`.`participial_type` = 'Modules\Game\Models\Side'
  ) as `joined_members`,
  (
    select
      composition
    from
      `rounds`
    where
      `sides`.`round_id` = `rounds`.`id`
    limit
      1
  ) as `round_composition`
from
  `sides`
where
  (
    `type` = 0
    or `type` = 1
  )
  and `sides`.`round_id` in (109)
having
  `joined_members` = `round_composition`

Case 2

Description

I try to count filled sides in fetched Round - i have correct result.

Code

$count = Round::find(109)->filledMainSides()->count();

Dump

Screenshot 2023-12-18 at 19 44 13

SQL

select * from `rounds` where `rounds`.`id` = 109 limit 1
select
  count(*) as aggregate
from
  (
    select
      `sides`.*,
      (
        select
          count(*)
        from
          `participants`
        where
          `sides`.`id` = `participants`.`participial_id`
          and `participants`.`participial_type` = 'Modules\Game\Models\Side'
      ) as `joined_members`,
      (
        select
          composition
        from
          `rounds`
        where
          `sides`.`round_id` = `rounds`.`id`
        limit
          1
      ) as `round_composition`
    from
      `sides`
    where
      `sides`.`round_id` = 109
      and `sides`.`round_id` is not null
      and (
        `type` = 0
        or `type` = 1
      )
    having
      `joined_members` = `round_composition`
  ) as `temp_table`

Case 3

Description

But, if i try to fetch Round with withCount of filled main sides, i will get count of all available sides.

Code

$round = Round::whereId(109)->with('filledMainSides')->withCount('filledMainSides')->first();

Dump

Screenshot 2023-12-18 at 19 57 48

SQL

select
  `rounds`.*,
  (
    select
      count(*)
    from
      `sides`
    where
      `rounds`.`id` = `sides`.`round_id`
      and (
        `type` = 0
        or `type` = 1
      )
  ) as `filled_main_sides_count`
from
  `rounds`
where
  `id` = 109
limit
  1
select
  `sides`.*,
  (
    select
      count(*)
    from
      `participants`
    where
      `sides`.`id` = `participants`.`participial_id`
      and `participants`.`participial_type` = 'Modules\Game\Models\Side'
  ) as `joined_members`,
  (
    select
      composition
    from
      `rounds`
    where
      `sides`.`round_id` = `rounds`.`id`
    limit
      1
  ) as `round_composition`
from
  `sides`
where
  (
    `type` = 0
    or `type` = 1
  )
  and `sides`.`round_id` in (109)
having
  `joined_members` = `round_composition`

Steps To Reproduce

To a greater extent, described in previous section. But if it needed, i can create demo.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions