简体   繁体   中英

Laravel ORM is giving an incorrect result on a simple query

I really don't get it, help me understand.

I'm writing an app which among other things calculates a NFL team "mark" (a mark is Win/Lose/Tie, sorry I don't know the word in English for that), so I have a "Marca" attribute in the Team model, looks like this:

public function getMarcaAttribute() {
    ...
}

Queries are very easy, first I get the number of games where the team is playing as local and calculate wins, loses and ties, for example, this one is for wins:

$gan += Juego::where('local', $this->id)
                  ->where('score_local', '>', 'score_visitante')
                  ->get()->count();

Then I do the same with games as visitor, but inverting comparison signs, of course.

Now, lets take a look at game Seattle (38) at Atlanta (25), if I do in the database

SELECT COUNT(*) FROM juegos WHERE local='atl' AND score_local > score_visitante;

Of course, it returns 0.

In the ORM, the generated query is:

  array (
    'query' => 'select * from `juegos` where `local` = ? and `score_local` > ? and `score_local` is not null',
    'bindings' => 
    array (
      0 => 'atl',
      1 => 'score_visitante',
    ),
    'time' => 0.89,
  ),

The thing is returning a 1. I even replaced the ->count()->get() for a ->get() and do a foreach over the results:

$gan = Juego::where('local', $this->id)
              ->where('score_local', '>', 'score_visitante')
              ->get();

Log::info('Ganados');
foreach ($gan as $g) {
  Log::info("$g->score_local > $g->score_visitante");
}

The thing is returning a line where it says "25 > 38"

I really don't understand what happens here. Any idea?

You can achieve this using whereRaw :

$gan = Juego::where('local', $this->id)
              ->whereRaw('score_local > score_visitante')
              ->get();

Or as suggested in comments, whereColumn :

$gan = Juego::where('local', $this->id)
              ->whereColumn('score_local', '>' , 'score_visitante')
              ->get();

@lagbox had it figured out in the comments. The problem is that by using prepared statements with bindings the term score_visitante is not been treated as a column name rather than a string.

There are several ways to solve it:

  1. Use Eloquent method whereColumn :
$gan += Juego::where('local', $this->id)
                  ->whereColumn('score_local', '>', 'score_visitante')
                  ->count();
  1. Use Eloquent method whereRaw :
$gan += Juego::where('local', $this->id)
                  ->whereRaw('score_local > score_visitante')
                  ->count();
  1. Use DB::raw :
$gan += Juego::where('local', $this->id)
                  ->where('score_local', '>', \DB::raw('score_visitante'))
                  ->count();

Note: You also do not need the ->get() before the ->count() . You can instruct the database to do the counting. This is particularly useful when the expected result is a large dataset with many rows, by returning just a number instead of potentially thousands or even millions of rows you are preserving your network resources.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM