count(): Parameter must be an array or an object that implements Countable
Asked Answered
W

15

45

I'm facing strange case. I face an error in production env not while in dev it's working fine.

Development: Laravel 5.4.28 PHP 7.0.13 MYSQL 5.7.17

Production: Laravel 5.4.28 PHP 7.2.1 MYSQL 5.7.20

In implementation code. I used:

namespace App;
use Illuminate\Support\Facades\Storage;
use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;

class Artwork extends Model
{
  use Searchable;

In development it works fine. But in production it gives me this error: count(): Parameter must be an array or an object that implements Countable in Builder.php (line 936)

as you can see in this pic:

enter image description here

Any idea what is the reason behind this? and how to fix?

Wondrous answered 19/1, 2018 at 14:43 Comment(6)
Can you post the entire stacktrace and the entrypoint in your code?Shanel
yes here is the full stack-trace: i.sstatic.net/85rR1.pngWondrous
can you paste the code in ArtworkController line 29?Barbirolli
Check this thread github.com/laravel/framework/issues/20248 It appears that in php 7.2, using count on null returns that error. can you downgrade to 7.1 perhaps?Pelt
yes in index function i use this line: $artworks = Artwork::orderBy('created_at', 'desc')->get();Wondrous
check this thread https://mcmap.net/q/64267/-phpmyadmin-count-parameter-must-be-an-array-or-an-object-that-implements-countableIssuable
G
72

Put this code at the beginning of your route file, it will work fine

if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
    error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}
Gillyflower answered 22/11, 2018 at 7:29 Comment(3)
OMG! What's going on with the development community nowadays? Hide the head in the sand is not a solution, fix the code is. Bad code IS bad code, despite you can hide the warnings or notices it generates. Do not use this method to "resolve" the issue. If you are using this it is because you updated your PHP in production without fully validating your code in a DEV/STAGE environment (bad move). However, if you tell me it is because your "shared hosting" updated their PHP to cause it in your site, you may use it as an emergency step while fixing the code, never beyond it! Then, change hosting!!!Latvian
@JulioMarchi It's happening exactly what's happening to the world. Hiding errors is simpler than fixing them. However, we use count($var) very often. I have used it 1653 times in one app code. The code breaks when the $var is null instead of being an array/object as expected. Fixing this inside 323 files isn't an easy thing either. Do I have another choice but to fix it? Not really if I want the app not breaking on my users. But still, hiding errors?Peshawar
This is called "Technical Debt", and the only way to deal with it is not to create it. In your case, YES, you are out of options. In fact, if you "assume" a variable will never be null (or unset) and recklessly call a count() function using it, then you most likely have much bigger problems all over your code beyond this example you gave... In terms of "Technical Debt", here you have a good article about the problem of allowing it to build: thedigitalinsider.com/…Latvian
T
53

This is a documented change in PHP 7.2. You need to either update Laravel to 5.6 or downgrade PHP to version 7.1.

Taken answered 12/3, 2018 at 22:1 Comment(4)
But Laravel 5.3 has SoftDeletes but also supports lowest PHP 5.6 Does that mean, I shouldn't use soft delete in my package up to Laravel 5.6?Riccardo
You should be using at least PHP 7.1 (as of March 2019). PHP 5.6 is no longer supported. This issue doesn't have anything to do with soft deletes as far as I can tell. We've had soft deletes since Laravel 4 and probably earlier.Taken
I know, but usecase kind complicated it. Am working on a package and I need to support older Laravel and PHP versions. For the moment I skipped soft deletes for the Laravel 5.3 - 5.5 which support lowest 5.6 - 7.0Riccardo
@BenHarold, may I ask you to take a look at a Laravel search related question here: #76486013 ?Kamseen
R
18

Replace

$originalWhereCount = count($query->wheres);

by

$originalWhereCount = count((array)$query->wheres);

in

\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php

Roseanneroseate answered 27/8, 2019 at 8:14 Comment(4)
NEVER modify files in the vendor/ directory.Taken
Ok, but how do you push your changes ? it is ignored alreadyThurmanthurmann
Old thread but this issue has been driving me nuts for 2 days. I wasn't noticing the problem on my remote server and php -v there was showing 7.2. On local machine I have php 7.2 so thought how can things be working on remote server if php 7.2 is being used. Turns out phpinfo shows 7.1. Didn't know the app and cli versions were different! So this Builder.php fix saved the day for now, though I realize better solution is upgrade laravel. But is php 7.2 really that much better than 7.1?Mcrae
> But is php 7.2 really that much better than 7.1? Yes, it offers many improvements, although by the time I'm writing this, PHP 7.4 is almost EOL and you should be moving towards PHP 8.1Taken
J
9

My server was on PHP 7.1 when I updated to PHP 7.2 I got the same issue.

After searching I found why this occurs. (This occurs because of a PHP update.).

so in my case, the error is solved by typecasting.

I just update all code where I used to count

Before

//this is before     
count($adminDetails)

After updated

//after update all i typecast all the code where i used count
count((array)$adminDetails)

Goodluck

Jallier answered 5/10, 2020 at 10:32 Comment(0)
L
8

I was facing similar issue in Laravel 5.6. Where I was getting error for object based array. I knew that data in that particular variable will always remain object so i used to convert the object to array. Here is code sample: $objectData = (array)$objectData; echo "Total Elements in array are: ".count($objectData);

Larynx answered 15/9, 2018 at 5:59 Comment(0)
N
8

This error occurs because you are using a higher PHP version and your Laravel application is on an older PHP version.

✅ Simple solution:

Open: app/Providers/AppServiceProvider.php

And in: public function register() { ... } function add following code:

if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
    error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}
Nureyev answered 6/12, 2021 at 4:51 Comment(0)
U
5

In php 7.2+ count does not work on relation objects, you need to use:

$model->relation()->exists()

Not this (less than php 7.2):

count($model->relation)
Unveil answered 15/5, 2020 at 18:38 Comment(1)
count() works in PHP 7.2+. His Laravel version is not matching with PHP version in production. It is already answered. https://mcmap.net/q/366020/-count-parameter-must-be-an-array-or-an-object-that-implements-countableLandscape
N
3

i ran into the same problem (PHP 7.2 + Laravel 5.3) but i don't see any "good" answers here. For me, the problem occurs when i tried to start a Builder from a scope method on the model: SomeModel::forUser() calls scopeForUser(). Trying to build a new Query, it trips on a count($this->wheres) that gets no initial value (null). Because the magic static call to the scope starts the builder, no other conditions have been placed in the object so the property is still null at that point.

i thought it's worth sharing my solution first, then perspective on why i consider it better than Ben's answer. It's not personal, i just disagree.

Solution

i took a cue from this answer about overriding some of the core Illuminate\Database classes...

  1. Extend Illuminate\Database\Eloquent\Model
    Mine is App\Overrides\Database\Eloquent\Model
  2. Extend Illuminate\Database\Eloquent\Builder
    Mine is App\Overrides\Database\Eloquent\Builder
  3. Extend Illuminate\Database\Query\Builder
    Can you guess? App\Overrides\Database\Query\Builder
  4. Tell Laravel to use YOUR Eloquent\Model:
    config/app.php 'aliases' array, replace the 'Eloquent' value
    with your Eloquent\Model FQN

My Model:

namespace App\Overrides\Database\Eloquent;

/*
 * Notes:
 * * Using replacement Query\Builder with ALIAS
 * * Use of Builder in this class is MY Eloquent\Builder
 */
use App\Overrides\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Model as EloquentModel;

class Model extends EloquentModel
{
    public function newEloquentBuilder($query)
    {
        return new Builder($query);
    }

    protected function newBaseQueryBuilder()
    {
        $conn = $this->getConnection();

        $grammar = $conn->getQueryGrammar();

        return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
    }
}

My Eloquent\Builder:

namespace App\Overrides\Database\Eloquent;

use Illuminate\Database\Eloquent\Builder as EloquentBuilder;

class Builder extends EloquentBuilder
{
    public function __construct($query)
    {
        parent::__construct($query);

        /*
         * FIX #1: Set properties treated AS arrays
         *         to empty arrays on construct.
         */
        $this->wheres = [];
        // Any other properties treated as arrays should also be initialized.
    }
}

My Query\Builder:

namespace App\Overrides\Database\Query;

use Illuminate\Database\Query\Builder as QueryBuilder;

class Builder extends QueryBuilder
{
    public function __construct()
    {
        parent::__construct(...func_get_args());

        /*
         * FIX #2: Set properties treated AS arrays
         *         to empty arrays on construct.
         */
        $this->wheres = [];
        // Any other properties treated as arrays should also be initialized.
    }
}

This safely preserves the framework's functionality, since the only actual change you're making is initializing properties that should have been in the first place. Everything else will pass instanceof checks used for dynamic loading and dependency injection.

Opinion

While i agree with @ben-harold about every comment he made saying "NEVER edit vendor code," i disagree with the "solution." It's an oversimplification to a much more complex problem.

Upgrade Laravel: to ensure support for PHP 7.2, jumping up several minor versions - if not major releases - is impractical for a lot of teams. As a long term objective, yes of course. As something i can do to get rid of the bug for my deadline? Nope. Upgrading takes a lot of planning and frequently a lot of rewrites as structures, names, and functionality change. It's something to prioritize, but not a need-it-now answer.

Downgrade PHP: same problem. Downgrading into PHP 5.x means A) PHP is EOL, which may be a deal breaker for a lot of customers who have security policies, and B) any usage of PHP 7.x language features have to be scrapped. As with upgrading the framework this is very likely to cause a lot of headaches. It's also an even less useful solution, since walking backward in the language just puts you farther behind and will require more long-term effort.

Noctambulous answered 29/5, 2020 at 21:38 Comment(0)
B
1

place the below line ob code before the class name in your controllers

if (version_compare(PHP_VERSION, '7.2.0', '>=')) {
// Ignores notices and reports all other kinds... and warnings
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
// error_reporting(E_ALL ^ E_WARNING); // Maybe this is enough
}
Brinna answered 7/11, 2018 at 3:2 Comment(0)
M
1

I was facing the same issue with an external created table (Not using migration or command), After creating the model, I just assigned a table name, but the problem was in my model protected $fillable where I assign string instead of array and error occurred. There is 2 possible solution for that.

  1. Assign an array to your protected $fillable = ['filed1', 'filed2'];
  2. Remove protected $fillable completely (Not Recommended)
class Abc extends Model
{
     protected  $table = 'cities';
     protected $fillable = ['field1','field2', ...];
}
Mild answered 7/11, 2019 at 9:33 Comment(0)
A
1

Model looking for countable parameter:

class ClassName extend Model {
    protected $fillable=['column_name']; // column in DB of Model is in array
}
Angkor answered 25/1, 2020 at 10:29 Comment(0)
G
0

Before

count($collection['colors'])

Error:Expected type 'Countable|array'. Found 'string'

After

count((array)$collection['colors'])

It works for me!

Gaffney answered 11/9, 2022 at 11:11 Comment(0)
B
-1

'vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php' to:

$originalWhereCount = is_array($query->wheres) ? count($query->wheres) : 0;
Bite answered 30/12, 2019 at 7:34 Comment(1)
No! Never change vendor files!Taken
P
-1

I;m using laravel 6.x for this case you can use this way:

 $id = \DB::table('xxxx')->where('id', $id)->count();
Protochordate answered 25/11, 2020 at 8:45 Comment(0)
D
-3

I Solve this in Laravel 5.6

// in controller

public function index()
{
$todos = Todo::all();
return view('todos.index')->with(['todos' => $todos]);

}

// in view page

@if(count($todos) > 0)
  @foreach($todos as $todo)
    <div class="well">
      <h3>{{$todo->text}}</h3>
      <span class="label label-danger">{{$todo->due}}</span>
    </div>
  @endforeach
@endif
Deakin answered 17/5, 2018 at 4:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.