Если вы когда-либо использовали каркас Backbone для JavaScript, вы уже знакомы с Underscore. Действительно, это стало невероятно полезным для разработчиков JavaScript в целом. Но знаете ли вы, что он был перенесен на PHP?
В этой статье я расскажу о Underscore, о том, что он может сделать, и приведу несколько примеров того, как это может быть полезно.
Что такое Подчеркивание?
Underscore описывает себя как «библиотеку служебных поясов для JavaScript, которая обеспечивает большую поддержку функционального программирования, которую вы ожидаете в Prototype.js (или Ruby), но без расширения каких-либо встроенных объектов JavaScript. Это — связь, чтобы согласиться с смокингом jQuery и подтяжками Backbone.js. ”
В частности, Underscore предоставляет набор утилит для работы с коллекциями и массивами, некоторые для работы с объектами, базовую функциональность шаблонов и ряд других полезных функций.
Функции, которые работают с коллекциями и массивами, могут быть особенно полезны при работе с JSON, что делает его отличным для обработки ответов от веб-сервисов.
Установка / Загрузка
Самый простой способ загрузить библиотеку — использовать Composer :
{ "require": { "underscore/underscore.php": "dev-master" } }
Кроме того, вы можете скачать его вручную или клонировать с Github — вам нужен только один файл underscore.php
.
Синтаксис PHP Underscore
В исходной библиотеке JavaScript все функции Underscore имеют префикс подчеркивания и точку; например, _.each
, _.map
, _.reduce
. В PHP подчеркивание обычно резервируется как псевдоним gettext()
(для перевода строк), поэтому вместо него используется двойное подчеркивание.
Все функции, входящие в состав библиотеки, доступны как статические методы класса с именем __
то есть с двойным подчеркиванием.
Итак, если мы хотим отобразить функции JavaScript в их эквивалент PHP:
JavaScript PHP _.each __::each _.map __::map _.reduce __::reduce
…и так далее.
Вы также можете использовать его объектно-ориентированным способом;
__(array(1, 2, 3))->map(function($n) { return $n * 2; });
Эквивалентно:
__::map(array(1, 2, 3), function($n) { return $n * 2; });
Для целей этой статьи я собираюсь использовать стиль статического метода.
Работа с коллекциями и массивами
каждый
Вы можете перебирать массив, применяя функцию к каждому элементу, используя __::each
.
Например:
$items = array(1, 2, 3, 4, 5); __::each($items, function($item) { print $item; });
Производит:
12345
Вот (немного) более полезный пример:
$student_records = array( array( 'name' => 'Joe Bloggs', 'id' => 1, 'grade' => 72, 'class' => 'A', ), array( 'name' => 'Jack Brown', 'id' => 2, 'grade' => 67, 'class' => 'B', ), array( 'name' => 'Jill Beaumont', 'id' => 3, 'grade' => 81, 'class' => 'B', ), ); __::each($student_records, function($record) { print $record['name'] . ' ' . $record['grade'] . '<br />'; });
Это производит:
Joe Bloggs A Jack Brown B Jill Beaumont B
Позже мы рассмотрим некоторые лучшие способы сделать что-то подобное, когда мы углубимся в шаблоны.
срывать
Если у вас есть многомерный массив, и вы хотите «вынуть» определенные значения и сгладить их в одномерный массив, вы можете использовать __::pluck
.
API Facebook предоставляет реальный пример того, когда это может быть полезно. Когда вы запрашиваете список друзей пользователя Facebook, результат (когда json_decode
переходит в многомерный массив) выглядит следующим образом:
$response = array( 'data' => array( array( 'name' => 'Joe Bloggs', 'id' => 123456789, ), array( 'name' => 'Jack Brown', 'id' => 987654321, ), ) ...
Если мы хотим извлечь идентификаторы пользователей Facebook в одномерный массив, мы можем сделать это:
$ids = __::pluck($response['data'], 'id'); // array(123456789, 98765432)
Минимум и максимум
Основываясь на наших образцах студенческих записей ранее, мы могли найти ученика с наивысшей оценкой, используя __::max
:
__::max($student_records, function($student) { return $student['grade']; }); // returns array('name' => 'Jill Beaumont', 'id' => 3, 'grade' => 81, 'class' => 'B')
Или самый низкий, используя __::min
:
__::min($student_records, function($student) { return $student['grade']; }); // returns array('name' => 'Jack Brown', 'id' => 2, 'grade' => 67, 'class' => 'B')
Как вы можете видеть, вместо того, чтобы просто возвращать наивысшую или наименьшую оценку, эти функции возвращают соответствующий элемент из массива — т.е.
Отфильтровать и отклонить
Метод filter
запускает проверку истинности для коллекции или массива и возвращает только те элементы, для которых он проходит.
В качестве примера, давайте вернемся к нашим студенческим записям из более ранних. Предположим, что оценка — 70 или выше. Мы можем использовать __::filter
чтобы применить простую функцию, чтобы мы могли узнать, кто из студентов сдал:
$passed = __::filter($student_records, function($student) { return $student['grade'] >= 70; });
Функция reject
просто противоположна filter
; это исключает предметы, которые проходят тест на правду.
Другими словами, следующие две функции дают одинаковый результат:
__::filter($student_records, function($student) { return $student['grade'] >= 70; }); __::reject($student_records, function($student) { return $student['grade'] < 70; });
Сортировать по
Функция sortBy
упорядочивает массив — в порядке возрастания — используя функцию итератора. Вот простой пример:
$scores = array(476, 323, 1010, 567, 723, 1009, 600); $sorted = __::sortBy($scores, function($score) { return $score; });
Чтобы отсортировать в порядке убывания, просто отрицайте значение. Итак, чтобы получить список записей, начиная со студента, который набрал наибольшее количество очков:
$ordered = __::sortBy($student_records, function($student) { return -$student['grade']; });
группа по
Предположим, мы хотим реорганизовать нашу группу студентов в соответствии с классом, в котором они находятся.
Вот где приходит groupBy
. Мы можем сделать это:
var_dump( __::groupBy($student_records, 'class') );
Который будет производить следующий вывод:
array(2) { ["A"]=> array(1) { [0]=> array(4) { ["name"]=> string(10) "Joe Bloggs" ["id"]=> int(1) ["grade"]=> int(72) ["class"]=> string(1) "A" } } ["B"]=> array(2) { [0]=> array(4) { ["name"]=> string(10) "Jack Brown" ["id"]=> int(2) ["grade"]=> int(67) ["class"]=> string(1) "B" } [1]=> array(4) { ["name"]=> string(13) "Jill Beaumont" ["id"]=> int(3) ["grade"]=> int(81) ["class"]=> string(1) "B" } } }
уменьшить
Функция reduce
используется для сокращения коллекции в одно значение.
Например, чтобы получить сумму одномерного массива:
__::reduce(array(1, 2, 3), function($first, $second) { return $first + $second; }, 0); // 6
Если мы совмещаем reduce
с pluck
в наших студенческих записях, мы можем узнать среднюю оценку:
$average = round( ( __::reduce(__::pluck($student_records, 'grade'), function($first, $second) { return $first + $second; }, 0) / count($student_records) ), 2);
Здесь мы извлекаем оценки как одномерный массив, используя pluck
, сводя их к одному значению, используя простую аддитивную функцию итератора, делим его на количество записей и затем округляем до двух цифр.
найти
Функция find
перебирает массив, выполняя функцию для каждого элемента, пока функция не вернет true — другими словами, она возвращает первую соответствующую «запись».
Например, чтобы найти первого ученика с оценкой ниже 70, вы можете сделать это:
__::find($student_records, function($student) { return $student['grade'] < 70; })
Это должно привести к следующему, если вы var_dump
результаты:
array(4) { ["name"]=> string(10) "Jack Brown" ["id"]=> int(2) ["grade"]=> int(67) ["class"]=> string(1) "B" }
Предположим, мы хотим найти запись студента по ID. Мы можем сделать это:
function findById($records, $id) { return __::find($records, function($record) use ($id) { return ($record['id'] == $id); }); }
Если вы запускаете:
var_dump(findById($student_records, 2));
Вы должны получить это:
array(4) { ["name"]=> string(10) "Jack Brown" ["id"]=> int(2) ["grade"]=> int(67) ["class"]=> string(1) "B" }
Обратите внимание на включение ключевого слова use
, так что $id
находится в области видимости.
Шаблонирование
Одна из областей, где Backbone широко использует Underscore, — это простая функциональность шаблонов.
Как правило, он намного чище, чем, скажем, конкатенация строк, и вы можете комбинировать его с другими функциями Underscore, такими как __::each
чтобы сделать его еще более мощным.
Внутри строки шаблона вы можете отображать такие значения:
<%= $student['name'] %>
Вы можете выполнить код, используя этот синтаксис:
<% __::each($records, student) { %> … <% }) %>
Существует два распространенных метода шаблонизации. Один из них — определить его как строку, используя приведенный выше синтаксис для вставки значений или кода, и запустить его через функцию __::template()
:
$welcome = 'Hello <%= $name %>, welcome back!'; print __::template($welcome, array('name' => 'Jack'));
В качестве альтернативы, вы можете «скомпилировать» шаблон, определив переменную и присвоив результат функции __::template
, с шаблоном, определенным как один строковый аргумент.
Следующее эквивалентно предыдущему примеру:
$compiled = __::template('Hello <%= $name %>, welcome back!'); print $compiled(array('name'=>'Jack')); // Hello Jack, welcome back!
Вот как вы можете создать простой шаблон для вывода неупорядоченного списка, комбинируя __::template
с __::each
:
$ul = __::template('<ul><% __::each($items, function($item) { %><li><%= $item %></li><% }); %></ul>'); print $ul(array('items' => array('one', 'two', 'three')));
Давайте создадим скомпилированный шаблон, который берет набор студенческих записей и создает неупорядоченный список их имен:
$list_students = __::template('<ul><% __::each($records, function($student) { %><li><%= $student["name"] %></li><% }); %></ul>');
Затем, чтобы сделать это:
print $list_students(array('records' => $student_records));
Вы должны получить следующий вывод:
<ul> <li>Joe Bloggs</li> <li>Jack Brown</li> <li>Jill Beaumont</li> </ul>
Или шаблон для составления таблицы учеников и их оценок:
$grades_table = __::template('<table><thead><tr><td>Student</td><td>Grade</td></tr></thead><tbody><% __::each($records, function($student) { %><tr><td><%= $student["name"] %></td><td><%= $student["grade"] %>%</td></tr><% }); %></tbody></table>'); print $grades_table(array('records' => $student_records));
Конечно, вы можете передать несколько аргументов, чтобы мы могли добавить заголовок таблицы, например:
$grades_table = __::template('<h4><%= $title %></h4><table><thead><tr><td>Student</td><td>Grade</td></tr></thead><tbody><% __::each($records, function($student) { %><tr><td><%= $student["name"] %></td><td><%= $student["grade"] %>%</td></tr><% }); %></tbody></table>'); print $grades_table(array('title' => $title, 'records' => $student_records));
Расширение подчеркивания
Вы даже можете создавать свои собственные функции, используя миксины.
__::mixin(array( 'capitalize'=> function($string) { return ucwords($string); }, 'shout' => function($string) { return strtoupper($string); } )); __::capitalize('joe bloggs'); // 'Joe Bloggs' __::shout('Joe bloggs'); // 'JOE BLOGGS'
Резюме
В этой статье я познакомил вас с портом PHP популярной библиотеки «утилита пояса», Underscore. Я прошел через некоторые из доступных функций; Однако есть еще много всего, что можно исследовать. Просмотрите документацию и поиграйте!