Статьи

Начало работы с PHP Underscore

Если вы когда-либо использовали каркас 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. Я прошел через некоторые из доступных функций; Однако есть еще много всего, что можно исследовать. Просмотрите документацию и поиграйте!