Пишем composer либу (slug helper)

Иконка composer и программист который разрабатывает библиотеку

Всякий раз когда нужно создать веб приложение, я добавляю slug. Пришло время, не искать по проектам реализацию этого функционала. Пришло время завернуть это в библиотеку и добавлять её в проект простой установкой через composer.

Я уже создал две библиотеки: для перевода текста, через онлайн сервисы или транслитерации офлайн и для создания валидного слага. Позже, я напишу отдельную статью об этом.

В этом примере, мы создадим обертку, библиотеку которая объединит эти сервисы. На вход она будет получать тайтл статьи, далее, текст будет переведен на английский язык и уже на его основе будет создан слаг. Мы так же реализуем цепочку вызовов, если один онлайн сервис будет не доступен, текст будет передан другому. К сожалению, на данный момент мои наработки по сервисам перевода яндекса и гугла, уже не актуальны, яндекс уехал в облако, гугл уже лет 8 не предоставляет бесплатного апи для перевода, возможно яндекс ушел в след за ним, я проверю это позже. Хорошо, что один сервис онлайн перевода работает (бесплатно) без изменений и для наших целей его более чем достаточно.

Немного о slug

Slug в контексте веб-разработки — это часть URL, которая используется для идентификации конкретной страницы на сайте. Slug оформляется таким образом, чтобы по url можно было понять о чем идет речь на странице.

Несколько ключевых характеристик

  • Slug состоит из слов английского языка, которые описывают содержимое страницы. Например, ссылка текущей статьи https://treecode.ru/article/5/creating-composer-library, часть creating-composer-library является slug, который описывает, что на странице можно найти информацию о создании библиотеки composer. Так же, мы избегаем кодирования символов ссылки типа: %D0%B7%D0%B0%D0%BA%D0%BE%D0%B4%D0%B8%D1%80%D1%83%D0%B9%20%D1%8D%D1%82%D0%BE%D1%82%20%D1%82%D0%B5%D0%BA%D1%81%D1%82.
  • Slug не содержит пробелов, специальных символов или тех, что могут нарушить структуру url. Вместо пробелов в основном используются дефисы. Это всё улучшает безопасность.
  • Slug может улучшить позиционирование страницы в поисковых системах, так как поисковые системы используют текст в url для понимания и ранжирования страниц.

Простетский пример: Привет, Мир! -> hello-world

Понеслась

  • PHP 8.1
  • Composer 2.5.1

Репозиторий в github мы создадим позже.

Создаем директорию инитим гит и композер. Мой пример основан на текущей разработке библиотеки, в момент написания статьи.

mkdir TranslatedSlugHelper && cd TranslatedSlugHelper

Я решил использовать CamelCase, вместо привычного snake_case для именования файлов и директорий и ничего вы мне не сделаете.

git init

Мне тут гит предложил определить имя ветки по-умолчанию. Ничего не меняем, сделаем это потом, если потребуется. Пусть будет master.

composer init

Эта команда создаст composer.json. Но мы же не снарядили телегу в дорогу! Я устанавливал composer прямо по официальной доке

Итак, композер задаст нам ряд не скромных вопросов. На первый же, я уверено отвечаю названием пакета, оно будет отображатся на packagist, по нему будут производится всякие манипуляции типа require или remove.

Package name (<vendor>/<name>) [system_user/translated-slug-helper]: abeliani/translated-slug-helper

Можно просто enter протапать пока вопросы не закончатся, потом отредактировать composer.json. Я так и сделаю! Вот мне еще сейчас не хватало придумывать описание библиотеки.

В директории с новосозданной библиотекой и файлом composer.json устанавливаю первые зависимости. Первой будет библиотека перевода или транслитерации текста.

composer require abeliani/string-translator:v1.0.0 --ignore-platform-reqs.

--ignore-platform-reqs. Позволяет игнорировать системные зависимости. Раньше у меня вообще локально пыха была не установлена, но для простоты я её воткнул в систему, но без излишеств. В докере, на хостинге, в облаке, на виртуалке все системные зависимости будут уже установлены и этот аргумент не потребуется.

Обнаружились некоторые особенности. Я переименовал библиотеку на github в text-tranlsator, но на packagist, не обновляется информация по имени пакета, даже после удаления/добавления ничего не изменилось. Так же, описание берется из README.md ветки defeault. При переключении тега, описание подтягивается из то же дефолтной ветки. Так, что лучше быть тут внимательнее.

Продолжаем

Добавим файл .gitignore. Нам не нужно отправлять в git служебные файлы и директории, гит свою прячет сам, остальное нужно игнорить нам. Как минимум, для нашей library, заигнорим vendor и composer.lock. Игнор последнего, позволит быблиотеке более гибко втыкаться в проекты пользователя.

vendor/composer.lock

В моем случае, обе библиотеки лежат локально и были созданы аналогичным в этой статье способом, мне не нужно переустанавливать зависимости при внесении изменений в одну из библиотек. Давайте, на примере slug-helper так и сделаем.
Склонируем репу abeliani/slug-helper на уровень выше.

git clone https://github.com/abeliani/slug-helper

В наш composer.json пропишем секцию. Где укажем путь до slug-helper. И в секцию require добавим поле slug-helper, при установке зависимостей, композер обратится в секцию repositories и создаст ссылку в директорию slug-helper, при любых изменениях в пакете slug-helper, они будут "видны" в нашей основной библиотеке. В качестве версии поставим *, поскольку мы просто пробрасываем локальный код в наш vendor.

Символические ссылки могут быть запрещены политиками безопасности или у вашего пользователя может не быть прав для их создания, тогда композер скопирует код библиотеки в vendor. Для проверки, что все пробросилось и именно по ссылке, перейдем cd vendor/abeliani и...

Для linux и macos выполним:

ls -l

Для windows:

dir

В терминале мы получим список директорий. Возле директории переданной по ссылке, будет отображен её путь. Так же, если ссылка не валидна или не доступна, она будет отмечена красным цветом в списке.

slug-helper -> ../../../SlugHelper/

Вот, что в итоге можно любить и жаловать. (Докинул две библиотеки для работы с тестами codeception/codeception и codeception/module-asserts)

Главное тут, это type: library, определяем свой пакет как библиотеку. License я поставил MIT (коротко - делайте, что хотите).

{    "name": "abeliani/translated-slug-helper",    "description": "Helps to get a valid slug translated or transliterated by a driver collection",    "type": "library",    "license": "MIT",    "autoload": {        "psr-4": {            "Abeliani\\TranslatedSlugHelper\\": "src/"        }    },    "autoload-dev": {        "psr-4": {            "Abeliani\\SlugHelper\\Tests\\": "tests/"        }   },    "authors": [        {            "name": "Anatolii Belianin"        }    ],    "repositories": [   	{   		"type": "path",   		"url": "../slug-helper"   	}    ],    "minimum-stability": "stable",    "require": {        "abeliani/string-translator": "v1.0.0",        "abeliani/slug-helper": "*"    },    "require-dev": {        "codeception/codeception": "^4.1",        "codeception/module-asserts": "^2.0"    }}

Всё равно, мы это всё на github закинем, итоговую версию можно будет глянуть там. Так же, я определил namespace нашей библиотеки src и сделал это (не обязательно) для директории с тестами. По этому NS наша библиотека будет использоваться в сторонних проектах как зависимость.

Класс библиотеки

Вот тут статья слишком затянулась. Подумал, что будет не лишним, показать и саму реализацию, потому как она не большая и из сонмы существующих библиотек, мы легко можем создавать свои обертки получая новый функционал. Начал с создания тестов, как истиный крутан и мисье. Всё шло хорошо, пока это не растянулось больше чем на статью. Давайте, я создам канал на ютубе и там поговорим об этом подробнее. Сейчас сосредоточимся на composer, внезапно, о чем статья и задумывалась. Но это не хабр, тут главное, чтобы моей маме нравилось. ахахаха

Composer

В нашем composer.json есть секция require и require-dev, dev секция нужна только для разработки. При подключении библиотеки в проект, эта секция разворачиваться не будет. Чтобы устанавливать зависисмости в dev секцию, нужно добавить флаг --dev при установке, как это показано в примере установки codeception ниже.

composer require "codeception/codeception" --dev

Github

Заходим на гитхаб, создаем там репозиторий, я свой назвал translated-slug-helper, копируем ссылку на него. Теперь мы свой локальный код, свяжем с удаленным репозитоиием.

git remote add origin https://github.com/our/repo_url.git

Создадим токен доступа к нашим репозиториям, этот токен мы укажем вместо пароля, при отправке измнений в github. Какнить обзовем его, ткнём в крыжик repo, зададим год (не бесконечный, мы же красавцы) время жизни токена, сразу компирнем его, на экране, это отобразится один раз. В рамках данной статьи не будем разбираться с credential manager.

Теперь разберемся с нашими веточками и тегами. Нам локально по дефолту создало ветку master, а при создании репозитория удаленно на github, создается ветка main. Я буду использовать привычные для меня ветки master и develop. Develop ветка будет основной, в ней будут отображаться последние изменения, в ветке master будет лежать стабильный код последнего релиза, к нему так же будет привязан тег с версией, например v1.0.0.

Переименуем нашу локальную ветку master в develop. Закоммитим изменения и запушим develop в репу.

git branch -m master developgit commit -m "initial commit"git push -u origin develop

В интерфейсе гихаба, изменим дефолтную ветку на develop. Settings -> Default branch -> develop. Перейдем на главную, кликнем в выпадающий список веток, перейдем в полный список и удалим ветку main. Наведем порядок. Далее, создадим ветку мастер от develop и отправим её.

git checkout -b mastergit push origin master

Теперь у нас есть рабочая основная ветка develop и master в которой хранится (в идеале) только рабочий, стабильный код. При таком раскладе, предполагается, что мы будем создавать ветки от develop, создавать pull request, мержить это после ревью, при накоплении правок для нового релиза, делаем merge request из develoop в master, создаем новый тег и готово. Теги с постфикосм типа alfa, beta, можно делать с коммитами ветки develop. Кстати, создадим новый тег.

git tag -a v1.0.0 -m "first stable version"git push origin v1.0.0

Терерь заходим на packagist регаемся, кликаем submit, копируем ссылку на нашу репу из гита, Check -> Submit. Готово! Теперь скопируем команду установки прямо из нашего README и установим либу в наш проект!

Поздравляю, мы это сделали! Теперь вы сможете создавать свои библиотеки, допиливать существующие и делится этим со всем миром!

Успехов, в освоении навыков программирования.