Dependency Injection Containers (DI контейнеры) в Joomla 4
Этот текст - перевод статьи из нового портала документации для разработчиков Joomla, раздел "Основные концепции".
Введение
Joomla 4 внедряет практику контейнеров внедрения зависимостей (DI контейнеры, DIC) в Joomla. Эта статья призвана объяснить, почему мы внедряем их и как их использовать в Joomla.
DI контейнеры уже давно существуют в экосистеме PHP для поддержки целей внедрения зависимостей. Например, Symfony представила эту концепцию в 2009 году.
Есть несколько причин, по которым пришло время внедрить их в Joomla 4:
1. Тестирование — одной из тем Joomla 3 были глючные релизы. Нам нужно иметь возможность тестировать классы и компоненты более простым способом. Внедрение зависимостей позволяет значительно упростить внедрение классов Mock, что, мы надеемся, позволит нам уменьшить количество ошибок.
2. Нужно уменьшить количество магии в Joomla - Joomla имеет большое количество "волшебных" файлов, названия которых нужно угадывать. Это увеличивает количество времени, которое люди, плохо знакомые с Joomla, тратят на изучение соглашений по именованию файлов. Предоставление конкретного класса в расширениях позволяет нам легко тестировать совместимость расширений с другими расширениями (например, категориями и ассоциациями).
Глобальный контейнер
Внедрение глобального контейнера зависимостей очень слабо заменяет класс Factory (ex. JFactory). Однако его не следует путать с прямой заменой. Так, например, в ваших контроллерах в CMS вместо \Joomla\CMS\Factory::getDocument(); стоит использовать $this->app->getDocument();.
Это использует внедренное приложение и поэтому упрощает тестирование.
Ссылки по теме на Хабре:
- Опубликован скорректированный план выпуска релизов Joomla 4 и Joomla 5
Создание объекта в контейнере
Чтобы поместить что-то в глобальном DI-контейнере Joomla проще всего передать анонимную функцию. Пример для логгера ниже:
Функция share принимает два обязательных параметра и необязательный третий параметр:
1. $key - имя сервиса (dataStore key) - почти всегда является именем класса, который вы создаете.
2. $value - Анонимная функция принимает единственный параметр — экземпляр контейнера (это позволяет вам получать любые зависимости из контейнера). return — это сервис, который вы хотите поместить в контейнер.
3. $protected - (необязательный параметр) - это булев параметр, определяет, защищена ли служба от перезаписи (т. е. разрешено ли кому-либо еще переопределять ее в контейнере). Как правило, для основных служб Joomla, таких как объекты сессии (Session), это true.
Теперь рассмотрим более сложный пример:
Здесь видно, что мы добавили две вещи — начали использовать зависимости (роутер API получает приложение API из контейнера) и мы также создали алиас для ApiRouter (в Joomla 4 существует 5 типов приложений - Application - Site, Administrator, Cli, API и Installation, а также могут быть созданы свои типы - Т.С.). Это означает, что контейнер создает экземпляр ApiRouter тогда, когда распознает использование класса. Зато в нашем коде для простоты мы сможем запустить следующий вызов, чтобы получить роутер (That means whilst the container recognises that if it needs to build an ApiRouter instance it can do that. But in our code to keep things simple we can also run to retrieve our router).
Factory::getContainer()->get('AmazingApiRouter');
В то время как в Joomla наши провайдеры могут выглядеть более сложными, потому что логика создания объектов внутри анонимной функции более сложна - все они следуют этой базовой идее.
ПровайдерыПровайдеры в Joomla — это способ регистрации зависимости в сервис-контейнере. Для этого создайте класс, реализующий Joomla\DI\ServiceProviderInterface.
Это дает вам метод регистрации, который содержит контейнер. Затем вы можете снова использовать метод share, чтобы добавить любое количество объектов в контейнер. Затем вы можете зарегистрировать их в контейнере с помощью \Joomla\DI\Container::registerServiceProvider. Вы можете посмотреть, как мы регистрируем все сервис-провайдеры, здесь, в методе \Joomla\CMS\Factory::createContainer.
Контейнер компонентаКаждый компонент также имеет свой собственный контейнер (который находится в разделе администратора (administrator section) Joomla). Однако этот контейнер не подвергается воздействию. Он нужен только для того, чтобы получить системные зависимости и позволить классу представлять ваше расширение. Этот класс является классом Extension и как минимум должен реализовывать интерфейс соответствующего типа расширения. Например, компонент должен реализовать \Joomla\CMS\Extension\ComponentInterface (libraries/src/Extension/ComponentInterface.php). Для получения полной информации о реализации в Вашем расширении мы рекомендуем обратиться к официальной документации Joomla «Разработка компонента MVC для Joomla 4».
Использование контейнера компонента в другом расширении
Вы можете легко получить контейнер другого расширения через объект CMSApplication. Например, Factory::getApplication()->bootComponent('com_content')->getMVCFactory()->createModel('Articles', 'Site');
Получите контейнер com_content, получите MVC Factory и получите ArticlesModel фронтенда Joomla. И это будет работать в любом расширении во фронтенде, бэкэнде или API Joomla (в отличие от старого метода LegacyModel::getInstance()).ДополнительноВ документации Joomla Framework есть отличный пример того, почему внедрение зависимостей полезно для вашего приложения и как DIC помогает его структурировать. Читать на GitHub.
От переводчикаЗамечания и пожелания по переводу приветствуются.
4 крутые функции JavaScript, о которых не знает большинство junior разработчиков
С помощью JavaScript можно делать одно и то же разными способами. С выпуском каждой новой спецификации ECMAScript добавляются новые методы и операторы, чтобы сделать код более коротким и читабельным.
1. Object.entries
Большинство разработчиков используют метод Object.keys для итерации по объекту. Этот метод возвращает только массив ключей объекта, а не значения. Можно использовать Object.entries для получения как ключа, так и значения.
Чтобы выполнить итерацию по объекту, можем сделать следующее:
Оба подхода, описанные выше, возвращают один и тот же результат, но Object.entries позволяет легко получить пару ключ-значение.
2. Метод replaceAll
В JavaScript, чтобы заменить все вхождения строки другой строкой, нам нужно использовать регулярное выражение, подобное следующему:
Но в ES12 в String.prototype добавлен новый метод replaceAll, который заменяет все вхождения строки другим строковым значением:
3. Числовой разделитель
Можно использовать символ подчеркивания «_» в качестве числового разделителя, для упрощения подсчета количества нулей в числе.
Разделитель также можно использовать с числами BigInt, как в следующем примере:
Это делает число более читабельным.
4. document.designMode
Связанный с интерфейсным JavaScript, designMode позволяет редактировать любой контент на странице. Просто откройте консоль браузера и введите следующее:
Это полезно для дизайнеров, так как им не нужно каждый раз менять что-то в коде в соответствии с изменениями на экране.
Заключение
Рассмотренные трюки и фичи способны ускорить работу разработчика, а их использование не только необходимо, но и полезно. Продолжайте познавать скрытые возможности языка, изучать всевозможные трюки и повышать свой скилл, а мы поможем новым чтивом. Удачи!
Создание модулей с учётом новой структуры Joomla 4
Joomla 4 "под капотом" претерпела немало изменений относительно предыдущих версий. Её кодовую базу сообщество разработчиков регулярно подтягивают до современных реалий, вводя актуальные технологии в ядро CMS. Так, например, если раньше загрузка классов была вариациями на тему include, то в Joomla 4 появился лоадер, приведённый к PSR-4. Ядро CMS переводится на концепцию сервис-провайдеров, внедрены DI-контейнеры. Эти изменения влекут за собой изменения в структуре компонентов, модулей и плагинов.
В данной статье пойдёт речь о том, как создать модуль для Joomla 4 с новой структурой файлов и классов. Как создать новый (или апгрейдить старый) модуль так, чтобы он ещё долго прослужил на Joomla 4 и Joomla 5?
Статья на Хабре
Полоса прокрутки (scrollbar) - один из самых неиспользуемых элементов в веб-разработке. Как с ним работать?
Полоса прокрутки присутствует сегодня на любом сайте видимо или невидимо. Это вполне обыденный элемент, на который обычно никто не обращает особого внимания.
По моему же мнению - это отличный способ закрепить сайт в памяти пользователя, поэтому давайте научимся с ней работать.
Телеграм - https://t.me/havaevau_webstudy
Основы
Бывает, что содержимое элемента не помещается в нем, так как оно слишком велико. И чтобы сделать содержимое прокручиваемым, можно использовать overflow: auto. Благодаря ему полосы прокрутки будут отображаться по умолчанию.
Вы можете кастомизировать скроллбар используя свойства scrollbar с префиксом -webkit в сочетании с обычными свойствами ширины scrollbar-width и цвета scrollbar-color.
Хочу сказать про плагин postcss-scrollbar - он создает свойства скроллбара с префиксом -webkit из стандартных свойств. Это дает возможность получить получить кросс-браузерные стили, например через такой код:
.scroll-container {
overflow: auto;
scrollbar-width: thin;
scrollbar-color: hsl(0 0% 50%);
/* postcss-scrollbar will add the -webkit version automatically! */
}
Темы и scrollbar
На многих сайтах при переключении на темный режим скроллбары "застревают" в белом
Есть отличные свойства - color-scheme, через которые это можно исправить. Но юзать их вам придется не только в ситуациях с полосой прокрутки. Можете почитать об этом более подробно здесь - https://web.dev/color-scheme/
html {
/* defer to OS preference */
color-scheme: dark light;
/* override, assuming the theme toggler sets a data-theme attribute */
&[data-theme=light] { color-scheme: light; }
&[data-theme=dark] { color-scheme: dark; }
}
Как предотвратить смещение верстки
При использовании overflow: auto может возникнуть проблема - смещение верстки. Решить её поможет overflow: overlay - скроллбар никогда не будет занимать место и/или overflow: scroll - полоса прокрутки всегда будет на месте
А с помощью scrollbar-gutter: stable вы можете "сказать" браузеру, чтобы он зарезервировал место для полос прокрутки.
.scroll-container {
overflow: scroll;
@supports (scrollbar-gutter: stable) {
overflow: auto;
scrollbar-gutter: stable;
}
}
На этом все, прокачивайте свои скроллбары, пишите: был ли у вас опыт в их кастомизации?
Тип данных Object в JavaScript
Привет, в этом уроке мы основательно пройдемся по типу данных object, рассмотрим типы объектов, свойства, научимся создавать объекты и узнаем различия между ними, а также поговорим об операторе typeof и нюансах его работы. Текстовый урок в полной версии статьи.
Object
object - ссылочный тип данных, является то самостоятельной единицей, имеющей свойства и определённый тип.
У объекта есть свойства которые с ним ассоциируется. Свойство объекта можно понимать как переменные закрепленные за ним.
Для того чтобы создать объект у нас есть несколько способов.
Конструктор класса
// Вызов конструктора класса Object
let objectExample_1 = new Object();
// Далее наполняем его данными
objectExample_1.name = 'John';
objectExample_1.age = 22;
В примере выше мы создали внутри объекта свойства name и age и записали в них данные.
Литерал
Мы можем использовать более простой и удобный синтаксис для создания объектов.
let objectExample_2 = {}
// наполнение объекта данными
Если мы используем литеральный синтаксис, то можем создать объект сразу с значениями.
objectExample_2.name = 'Alice';
objectExample_2.age = 30;
console.log(objectExample_2)
let objectExample_3 = {Как видите, литеральный синтаксис намного удобнее нежели создание через конструктор класса. Его и будем использовать.
name: 'Vladimir',
age: 25
}
console.log(objectExample_3)
let user = {
firstName: 'John',
lastName: 'Doe',
age: 30,
gender: 'male'
}
Допустим я хочу получить имя данного пользователя.
console.log(user);
console.log(user.firstName);
Аналогичным образом я могу получить доступ ко всем значениям свойств объекта.
console.log(user.lastName);
console.log(user.age);
console.log(user.gender);
Синтаксис квадратных скобок - []
Ключи в объектах это всегда строки или Symbol, сосредоточимся пока на строках. Как мы знаем строки не всегда могу состоять из одного слова в котором нет пробелов и как быть если нам нужно создать свойство в объекте имя которого будет состоять из 2х слов разделенных пробелами или вообще будет являться выражением. Для этого будем использовать [ ].
let student = {Получить значения этих свойств у нас просто так не получится. Если мы будем использовать синтаксис точки '.' то получим ошибку. Вместо этого будем использовать квадратные скобки [ ].
firstName: 'John',
['last name']: 'Dou', // Имя свойства содержащее пробел
['student-' + 'age']: 30, // Вычисляемое имя свойства
}
console.log(student);
console.log(student.firstName) // Обычный способ
console.log(student['last name'])
console.log(student['student-age'])
Готово, теперь все работает!
Вложенные объекты
Также стоит понимать что если мы можем вкладывать примитивы в объекты, делая их значениями ключей, то аналогичным образом мы можем вкладывать и другим объекты.
const deepObject = {
name: 'Mazda',
model: '#1',
owner: {
firstName: 'John',
lastNam: 'Doe',
age: 12
}
}
console.log(deepObject)
Это простой пример, на самом деле вложенность может быть очень большой, важно понимать что так можно делать.
Array
array - массив данных, а также итерируемый объект, также относится к типу данных object. Ключи для элементов массива создаются автоматически и являются индексами, позицией элемента внутри массива.
Индексирование массива начинается с нуля.
const arr = ['John', 'Alice', 'Andrew', 'Antony'];
console.log(arr)
У массива есть длинна (length), это свойство содержит количество элементов массива.
console.log(arr.length);
Берем элементы по их индексу.
console.log(
arr[0], arr[1], arr[2], arr[3]
)
Если мы хотим гарантировано получить значение последнего элемента в массиве.
console.log(
arr[arr.length - 1]
)
В этом уроке мы не будем погружаться в массивы и их работу полностью, на данный момент стоит знать что массивы тоже относятся к типу данных object.
Функции
Функции - это участки кода которые можно переиспользовать, в этом уроке мы не будем их рассматривать подробно.
Для создания функции существует несколько способов ключевое слово function и стрелочный синтаксис.
function foo(a, b) {
return a + b;
}
const bar = (a, b) => {
return a - b;
}
console.log(foo);
console.log(bar);
Функции также являются объектами и относятся к типу данных object.
P.S. Подпишись на мой youtube и telegram канал чтобы регулярно получать новый контент.
Сможете найти на картинке цифру среди букв?
Справились? Тогда попробуйте пройти нашу новую игру на внимательность. Приз — награда в профиль на Пикабу: https://pikabu.ru/link/-oD8sjtmAi
Все о datalist, fieldset и button в HTML
Привет, в рамках этого урока мы разберемся оставшимися тегами формы, а именно: datalist, fieldset, legend, button. Также разберемся в нюансах и частоте использования этих тегов. Текстовая версия урока в полной версии этой статьи.
Datalist
<datalist> - содержит набор опций (<option>), доступных для выбора. Выбранное значение будет установлено для элемента <input>, с атрибутом list.
Не стоит использовать его вместо тега <select>, но можно использовать как вспомогательный тег для <input>. Причина в том что его сложнее обрабатывать через JavaScript.
<form action="">
<label for="ice-cream-choice">Choose a flavor:</label>
<input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice" />
<datalist id="ice-cream-flavors">
<option value="Chocolate">
<option value="Coconut">
<option value="Mint">
<option value="Strawberry">
<option value="Vanilla">
</datalist>
</form>
Fieldset
<fieldset> - существует для группировки <input> внутри тега <form>, а также помогает удобно управлять группой полей.
<form action="#">
<h3>Registration form</h3>
<fieldset>
<div>
<label for="name_input">First name:</label>
<input id="name_input" type="text" placeholder="Email">
</div>
<div>
<label for="surname_input">Surname:</label>
<input id="surname_input" type="text" placeholder="Surname">
</div>
</fieldset>
<br>
<fieldset disabled>
<div>
<label for="email_input">Email:</label>
<input id="email_input" type="text" placeholder="Email">
</div>
<div>
<label for="password_input">Password:</label>
<input id="password_input" type="text" placeholder="Password">
</div>
</fieldset>
</form>
У <fieldset> также есть несколько атрибутов, которые помогают управлять сразу всей группой <input>.
name - задает общее имя для группы
disabled - делает группу недоступной для редактирования
form - если <fieldset> лежит вне тега <form> то этот атрибут позволит вам связать их.
<form action="" id="example_form">
<h3>Example form</h3>
<label>
<input type="text" placeholder="Some example input">
</label>
</form>
<fieldset form="example_form">
<div>
<label for="name_2_input">First name:</label>
<input id="name_2_input" type="text" placeholder="Email">
</div>
<div>
<label for="surname_2_input">Surname:</label>
<input id="surname_2_input" type="text" placeholder="Surname">
</div>
</fieldset>
Legend
<legend> - Является заголовком для группы элементов, сгруппированных через <fieldset>
<form action="">
<h3>Another Example Form</h3>
<fieldset>
<legend>User info</legend>
<div>
<label for="name_3_input">First name:</label>
<input id="name_3_input" type="text" placeholder="Email">
</div>
<div>
<label for="surname_3_input">Surname:</label>
<input id="surname_3_input" type="text" placeholder="Surname">
</div>
</fieldset>
<br>
<fieldset disabled>
<legend>User credentials</legend>
<div>
<label for="email_3_input">Email:</label>
<input id="email_3_input" type="text" placeholder="Email">
</div>
<div>
<label for="password_3_input">Password:</label>
<input id="password_3_input" type="text" placeholder="Password">
</div>
</fieldset>
Button
<button> - Парный тег, который предназначен для создания кнопки, имеет схожие атрибуты с тегом <input>. Может использовать как внутри <form> заменяя собой <input type="button">, так и просто как отдельный элемент на странице.
<form action="">
<h3>Another Example Form</h3>
<fieldset>
<legend>User info</legend>
<div>
<label for="name_3_input">First name:</label>
<input id="name_3_input" type="text" placeholder="Email">
</div>
<div>
<label for="surname_3_input">Surname:</label>
<input id="surname_3_input" type="text" placeholder="Surname">
</div>
</fieldset>
<br>
<fieldset disabled>
<legend>User credentials</legend>
<div>
<label for="email_3_input">Email:</label>
<input id="email_3_input" type="text" placeholder="Email">
</div>
<div>
<label for="password_3_input">Password:</label>
<input id="password_3_input" type="text" placeholder="Password">
</div>
</fieldset>
<fieldset>
<legend>Form Controls</legend>
<button type="submit">Submit</button>
<button type="reset">Clear Form</button>
</fieldset>
</form>