【问题标题】:count(): Parameter must be an array or an object that implements Countablecount():参数必须是数组或者实现了Countable的对象
【发布时间】:2018-06-28 20:09:09
【问题描述】:

我正面临着奇怪的情况。我在生产环境中遇到错误,而不是在开发环境中它工作正常。

发展: Laravel 5.4.28 PHP 7.0.13 MYSQL 5.7.17

生产: Laravel 5.4.28 PHP 7.2.1 MYSQL 5.7.20

在实现代码中。我用过:

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

class Artwork extends Model
{
  use Searchable;

在开发中它工作正常。但在生产中它给了我这个错误: count():参数必须是数组或者实现了Countable的对象 在 Builder.php 中(第 936 行)

正如你在这张照片中看到的那样:

知道这背后的原因是什么吗?以及如何解决?

【问题讨论】:

  • 你能在你的代码中发布整个堆栈跟踪和入口点吗?
  • 是的,这里是完整的堆栈跟踪:i.stack.imgur.com/85rR1.png
  • 你能把代码粘贴到ArtworkController29行吗?
  • 检查此线程github.com/laravel/framework/issues/20248 似乎在 php 7.2 中,对 null 使用 count 会返回该错误。你能降级到 7.1 吗?
  • 是的,在索引函数中我使用这一行:$artworks = Artwork::orderBy('created_at', 'desc')->get();

标签: laravel laravel-5 laravel-scout


【解决方案1】:

将此代码放在 route 文件的开头,它将正常工作

if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
    error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
}

【讨论】:

  • 我想这不是最好的方法,但它就像一个魅力 =)。谢谢你。
  • 我喜欢这个解决方案,因为当我准备升级到最新版本时,它暂时解除了对我的阻止。
  • 有时这些捷径可以救命 :) 谢谢
  • 天啊!当今的开发社区正在发生什么?把头藏在沙子里不是解决方案,修复代码才是。坏代码就是坏代码,尽管您可以隐藏它生成的警告或通知。不要使用这种方法来“解决”问题。如果您正在使用它,那是因为您在生产环境中更新了 PHP,而没有在 DEV/STAGE 环境中完全验证您的代码(不好的举动)。但是,如果您告诉我这是因为您的“共享主机”更新了他们的 PHP 导致它出现在您的站点中,您可以在修复代码时将其用作紧急步骤,永远不要超出它!然后,换主机!!!
【解决方案2】:

这是documented change in PHP 7.2。您需要将 Laravel 更新到 5.6 或将 PHP 降级到 7.1 版。

【讨论】:

  • 但是 Laravel 5.3 有 SoftDeletes 但也支持最低的 PHP 5.6 这是否意味着,我不应该在 Laravel 5.6 之前的包中使用软删除?
  • 您应该至少使用 PHP 7.1(截至 2019 年 3 月)。不再支持 PHP 5.6。据我所知,这个问题与软删除没有任何关系。自 Laravel 4 甚至更早的版本以来,我们已经进行了软删除。
  • 我知道,但是用例有点复杂。我正在开发一个包,我需要支持旧的 Laravel 和 PHP 版本。目前我跳过了支持最低 5.6 - 7.0 的 Laravel 5.3 - 5.5 的软删除
【解决方案3】:

替换

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

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

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

【讨论】:

  • 切勿修改vendor/目录中的文件。
  • 好的,但是你如何推动你的改变呢?它已经被忽略了
  • 旧线程,但这个问题已经让我发疯了 2 天。我没有注意到远程服务器上的问题,并且 php -v 显示 7.2。在本地机器上,我有 php 7.2,所以想如果使用 php 7.2,远程服务器上的事情怎么办。原来 phpinfo 显示 7.1。不知道app和cli版本不一样!所以这个 Builder.php 修复现在节省了一天,尽管我意识到更好的解决方案是升级 laravel。但是 php 7.2 真的比 7.1 好很多吗?
【解决方案4】:

我在 Laravel 5.6 中遇到了类似的问题。基于对象的数组在哪里出现错误。我知道该特定变量中的数据将始终保留为对象,因此我曾经将对象转换为数组。这是代码示例: $objectData = (array)$objectData; echo "Total Elements in array are: ".count($objectData);

【讨论】:

    【解决方案5】:

    当我更新到 PHP 7.2 时,我的服务器使用的是 PHP 7.1 我遇到了同样的问题。

    经过搜索,我找到了发生这种情况的原因。 (这是由于 PHP 更新而发生的。)。

    所以在我的例子中,错误是通过类型转换解决的。

    我只是更新了我以前计数的所有代码

    之前

    //this is before     
    count($adminDetails)
    

    更新后

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

    祝你好运

    【讨论】:

      【解决方案6】:

      在 php 7.2+ count 对关系对象不起作用,你需要使用:

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

      不是这个(低于 php 7.2):

      count($model->relation)
      

      【讨论】:

      【解决方案7】:

      我遇到了同样的问题(PHP 7.2 + Laravel 5.3),但我在这里没有看到任何“好的”答案。对我来说,当我尝试从模型上的范围方法启动 Builder 时出现问题:SomeModel::forUser() calls scopeForUser()。尝试构建一个新查询时,它会在没有初始值 (null) 的 count($this->wheres) 上跳闸。因为对作用域的神奇静态调用启动了构建器,所以没有在对象中放置其他条件,所以此时属性仍然是 null

      我认为值得先分享我的解决方案,然后再看看为什么我认为它比 Ben 的答案更好。这不是个人的,我只是不同意。

      解决方案

      我从 this answer 那里得到了关于覆盖一些核心 Illuminate\Database 类的提示...

      1. 扩展Illuminate\Database\Eloquent\Model
        我的是App\Overrides\Database\Eloquent\Model
      2. 扩展Illuminate\Database\Eloquent\Builder
        我的是App\Overrides\Database\Eloquent\Builder
      3. 扩展Illuminate\Database\Query\Builder
        猜一下? App\Overrides\Database\Query\Builder
      4. 告诉 Laravel 使用你的 Eloquent\Model:
        config/app.php 'aliases' 数组,替换 'Eloquent'
        与您的Eloquent\Model FQN

      我的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());
          }
      }
      

      我的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.
          }
      }
      
      

      我的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.
          }
      }
      
      

      这可以安全地保留框架的功能,因为您所做的唯一实际更改是初始化本应放在首位的属性。其他所有内容都将通过用于动态加载和依赖注入的instanceof 检查。

      意见

      虽然我同意 @ben-harold 关于他所说的“永远不要编辑供应商代码”的每条评论,但我不同意“解决方案”。这是对更复杂的问题的过度简化。

      升级 Laravel:为确保对 PHP 7.2 的支持,升级几个次要版本(如果不是主要版本)对于许多团队来说是不切实际的。作为一个长期目标,当然可以。作为我可以做些什么来摆脱我的最后期限的错误?没有。随着结构、名称和功能的变化,升级需要大量的计划和频繁的重写。这是需要优先考虑的事情,但不是现在需要的答案。

      降级 PHP:同样的问题。降级到 PHP 5.x 意味着 A) PHP 是 EOL,这对于许多拥有安全策略的客户来说可能是一个交易破坏者,并且 B) 任何使用 PHP 7.x 语言功能的行为都必须废弃。与升级框架一样,这很可能会引起很多麻烦。这也是一个更没用的解决方案,因为在语言中倒退只会让你落后,需要更多的长期努力。

      【讨论】:

        【解决方案8】:

        我在使用外部创建的表时遇到了同样的问题(不使用迁移或命令), 创建模型后,我只是分配了一个表名,但问题出在我的模型protected $fillable 中,我在其中分配字符串而不是数组并发生错误。 有两种可能的解决方案。

        1. 将数组分配给您的protected $fillable = ['filed1', 'filed2'];
        2. 完全删除protected $fillable(不推荐)
        class Abc extends Model
        {
             protected  $table = 'cities';
             protected $fillable = ['field1','field2', ...];
        }
        

        【讨论】:

          【解决方案9】:

          寻找可数参数的模型:

          class ClassName extend Model {
              protected $fillable=['column_name']; // column in DB of Model is in array
          }
          

          【讨论】:

            【解决方案10】:

            将下面的 ob 代码放在控制器中的类名之前

            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
            }
            

            【讨论】:

              【解决方案11】:

              我正在使用 laravel 6.x 对于这种情况,您可以使用这种方式:

               $id = \DB::table('xxxx')->where('id', $id)->count();
              

              【讨论】:

                【解决方案12】:

                出现此错误是因为您使用的是更高的 PHP 版本,而您的 Laravel 应用程序使用的是旧的 PHP 版本。

                ✅简单的解决方案:

                打开:app/Providers/AppServiceProvider.php

                并在:public function register() { ... }函数中添加如下代码:

                if(version_compare(PHP_VERSION, '7.2.0', '>=')) {
                    error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
                }
                

                【讨论】:

                  【解决方案13】:

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

                  $originalWhereCount = is_array($query->wheres) ? count($query->wheres) : 0;
                  

                  【讨论】:

                  • 不!永远不要更改vendor 文件!
                  【解决方案14】:

                  我在 Laravel 5.6 中解决了这个问题

                  //在控制器中

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

                  //在视图页面中

                  @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
                  

                  【讨论】:

                    猜你喜欢
                    • 2019-08-03
                    • 2018-06-08
                    • 2018-09-22
                    • 1970-01-01
                    • 2018-11-26
                    • 2019-04-02
                    • 2020-04-27
                    • 2021-11-05
                    • 2019-10-04
                    相关资源
                    最近更新 更多