array_map并将2个参数传递给映射函数 - array_map():参数#3应该是一个数组

时间:2014-12-22 09:22:36

标签: php laravel laravel-4

我有一个看起来像这样的抽象类:

abstract class Transformer {

    /**
     * Transform a collection of items
     *
     * @param array $items
     * @param bool $format
     * @return array
     */
    public function transformCollection(array $items, $format)
    {
        return array_map([$this, 'transform'], $items, $format);
    }

    /**
     * Transform a item
     *
     * @param array $item
     * @param bool $format
     * @return mixed
     */
    public abstract function transform(array $item, $format);

}

然后我有以下实现它的类:

class ServiceLogTransformer extends Transformer {

    public function transform(array $service_log, $format = false)
    {
        return [
            'id'    => $service_log['id'],
            'date'  => $service_log['log_date'],
            'time'  => $service_log['log_time'],
            'type'  => ($format ? status_label($service_log['log_type']) : $service_log['log_type']),
            'entry' => $service_log['log_entry']
        ];
    }

}

当此代码运行时,我收到错误:

array_map():参数#3应该是一个数组

在类中调用array_map函数时,如何传递2个或更多参数?我检查了PHP文档,看起来这是允许的,但它不适用于我的Larave 4.2项目。

有什么想法吗?

4 个答案:

答案 0 :(得分:37)

请务必阅读文档:

http://php.net/manual/en/function.array-map.php

array array_map ( callable $callback , array $array1 [, array $... ] )

然后你传递bool $format作为参数

  

“调用array_map函数时,如何传递2个或更多参数   在课堂上?

我会使用use()语法

创建匿名函数
public function transformCollection(array $items, $format)
{
    return array_map(function($item) use ($format) {
        return $this->transform($item, $format);
    }, $items);
}

答案 1 :(得分:6)

您无法使用array_map将硬编码值传递给回调函数(通常称为currying或函数式语言中部分应用的函数)。 array_map采用的是可变数量的数组,所有数组都应具有相同数量的元素。每个数组的当前索引处的元素作为单独的参数传递给回调。所以,例如,如果你这样做:

$arr1 = [1, 2, 3, 4];
$arr2 = [2, 4, 6, 8];

$func = function ($a, $b) { return $a.'-'.$b; };


$arr3 = array_map($func, $arr1, $arr2);

你明白了:

['1-2', '2-4', '3-6', '4-8']

希望这解释了它背后的想法 - 你传入的每个数组都会将第一个数组中当前位置的元素作为相关参数传递给回调函数。

所以,正如我所说,它不能被用于传递静态'回调的值。但是,您可以通过动态定义匿名函数来自己完成此操作。在transformCollection方法中:

return array_map(function ($item) use ($format) {
    return $this->transform($item, $format);
}, $items);

答案 2 :(得分:3)

大多数答案表明,使用use关键字的匿名函数是将其他参数传递给其他callables的典型方法。

abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        return array_map(function($item) use ($format) {
            return $this->transform($item, $format);
        }, $items);
    }
}

最有可能的是,这种特殊情况更适合标准foreach循环array_map,因为它可能更有效,更容易阅读。这也可以防止您的索引重新编号,以及使用TraversableArrayAccess个项目。

abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        foreach($items as $key => $item) {
           $items[$key] = $this->transform($item, $format);
        }
        return $items;
    }
}

如果你真的,真的 让你的心灵开始使用array_map,匿名函数就不适合你的环境(即PHP 5.3之前的版本)并且您需要将$format作为第二个参数传递,然后您需要将$format转换为与$items长度相同的数组。

abstract class Transformer {
    public function transformCollection(array $items, $format)
    {
        // Fill an array of same length as $items with the $format argument.
        $format = array_fill(0, count($items), $format);
        return array_map([$this, 'transform'], $items, $format);
    }
}

修改

我最近意识到,由于您正在使用实例转换数据,因此还有其他选项可用。它涉及将$format存储到实例,可能使用setter,如果它作为参数提供,则覆盖它。这样,可以使用$this->format通过转换方法访问它。

abstract class Transformer {
    protected $format;

    public function setFormat($format)
    {
        $this->format = $format;
    }

    public function transformCollection(array $items, $format = null)
    {
        if (isset($format)) {
            $this->setFormat($format);
        }
        // ...
    }

    // ...
}

答案 3 :(得分:0)

这可能不适用于laravel 4.2 // php 5.3之前的版本(如Shaun所述),但对于遇到此问题的某些人可能会派上用场。

abstract class Transformer {

    /**
     * Transform a collection of items
     *
     * @param array $items
     * @param bool $format
     * @return array
     */
    public function transformCollection(array $items, $format)
    {
        $args = func_get_args();
        return $this->mapWith([$this, 'transform'], $args);
    }

    /**
     * @param callback<array|string> $callback
     * @param array $args first a collection to disect, then optional additional arguments to the callback
     * @return array
     */
    private function mapWith($callback, $args) {
        $data = array_shift($args);
        $closure = \Closure::fromCallable($callback);
        $scope = \is_array($callback) ? $callback[0] : null;
        return array_map(function ($item) use ($scope, $closure, $args) {
            array_unshift($args, $item);
            if (null !== $scope) {
                array_unshift($args, $scope);
                $closure = [$closure, 'call'];
            }
            return \call_user_func_array($closure, $args);
        }, $data);
    }

    /**
     * Transform a item
     *
     * @param array $item
     * @param bool $format
     * @return mixed
     */
    public abstract function transform(array $item, $format);

}

function status_label($index){return vsprintf('label: %s', [$index,]);}

#Then I have the following class that implements it:

class ServiceLogTransformer extends Transformer {

    public function transform(array $service_log, $format = false)
    {
        return [
            'id'    => $service_log['id'],
            'date'  => $service_log['log_date'],
            'time'  => $service_log['log_time'],
            'type'  => ($format ? status_label($service_log['log_type']) : $service_log['log_type']),
            'entry' => $service_log['log_entry']
        ];
    }

}


$logs = [
['id' => 123454, 'log_date'=>'20180926', 'log_time'=>'151913', 'log_type'=>'q', 'log_entry' => 'lorem',],
['id' => 353454, 'log_date'=>'20180926', 'log_time'=>'152013', 'log_type'=>'r', 'log_entry' => 'dolor',],
];

$slt = new ServiceLogTransformer();
$new = $slt->transformCollection($logs, false);
$lab = $slt->transformCollection($logs, true);
var_dump($logs);
var_dump($new);
var_dump($lab);

因此,这是动态使用,方法是使用php匿名函数下的Closure类的调用方法。如果回调是数组,则它将->call的范围绑定到第一个数组元素,该数组元素应该是方法的对象。