<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>GromIT</title><generator>teletype.in</generator><description><![CDATA[GromIT]]></description><image><url>https://teletype.in/files/4c/2b/4c2b50a3-a66e-4135-b89b-5bf4fff7f176.jpeg</url><title>GromIT</title><link>https://blog.grom-it.ru/</link></image><link>https://blog.grom-it.ru/?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/gromit?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/gromit?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Mon, 20 Apr 2026 00:56:11 GMT</pubDate><lastBuildDate>Mon, 20 Apr 2026 00:56:11 GMT</lastBuildDate><item><guid isPermaLink="true">https://blog.grom-it.ru/Uzs4ynPVlar</guid><link>https://blog.grom-it.ru/Uzs4ynPVlar?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/Uzs4ynPVlar?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Поддержка внутренней документации к REST API в OctoberCMS</title><pubDate>Fri, 28 May 2021 12:59:52 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/8d/80/8d804592-164b-47f8-851d-e66cf908270e.jpeg"></media:content><category>OctoberCMS</category><description><![CDATA[<img src="https://teletype.in/files/89/fb/89fbc8fb-0dd9-4bd2-9570-7e74bf570d83.jpeg"></img>Так получилось, что некоторые из последних проектов в нашей компании были выполнены как REST-like API на OctoberCMS + фронтенд на Nuxt.js.]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/89/fb/89fbc8fb-0dd9-4bd2-9570-7e74bf570d83.jpeg" width="1200" />
  </figure>
  <h2>Рождение идеи.</h2>
  <p>Так получилось, что некоторые из последних проектов в нашей компании были выполнены как REST-like API на OctoberCMS + фронтенд на Nuxt.js.</p>
  <p>Я разрабатывал первую часть и мне надо было как-то рассказывать frontend-разработчику какие роуты есть у нас в API. Для этого я поддерживал коллекцию роутов в Postman. Всё шло не плохо, пока мне, наконец, это не надоело. Тогда меня посетила мысль: &quot;зачем мне поддерживать коллекцию в постмане, если у меня в коде уже всё есть?&quot;. </p>
  <p>Так родилась идея создания <a href="https://github.com/gromitsoft/oc-routesbrowser-plugin" target="_blank">плагина для OctoberCMS</a>, про который я расскажу в этой статье.</p>
  <h2>Плагин GromIT.RoutesBrowser</h2>
  <p>Итак <strong>GromIT.RoutesBrowser</strong> - это плагин, который отображает в админке на отдельной странице все роуты вашего приложения (за исключением системных, и роутов темы) с документацией, собираемой из кода и докблоков с помощью Reflection API.</p>
  <p>Самый простой способ понять, как с этим работать - это создать плагин с файлом <code>routes.php</code> и добавить какой-нибудь роут. </p>
  <p>Например, что-то такое:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253C%253Fphp%250A%250ARoute%253A%253Aget%28%27%252Fhello%252F%257Bname%257D%27%252C%2520%255BGreetController%253A%253Aclass%252C%2520%27greet%27%255D%29%253B%250A%250Aclass%2520GreetController%2520extends%2520%255CIlluminate%255CRouting%255CController%250A%257B%250A%2520%2520%2520%2520%252F**%250A%2520%2520%2520%2520%2520*%2520Returns%2520greeting%250A%2520%2520%2520%2520%2520*%250A%2520%2520%2520%2520%2520*%2520%2540param%2520string%2520%2524name%2520Person%2520name%250A%2520%2520%2520%2520%2520*%252F%250A%2520%2520%2520%2520public%2520function%2520greet%28GreetRequest%2520%2524request%252C%2520string%2520%2524name%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2524greeting%2520%253D%2520%28%2524request-%253Egreeting%2520%253F%253A%2520%27Hello%27%29%2520.%2520%27%252C%2520%27%2520.%2520%2524name%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520if%2520%28%2524request-%253Efriend%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2524greeting%2520.%253D%2520%27%2520and%2520%27%2520.%2520%2524request-%253Efriend%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520response%28%29-%253Ejson%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27greeting%27%2520%253D%253E%2520%2524greeting%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%29%253B%250A%2520%2520%2520%2520%257D%250A%257D%250A%250A%252F**%250A%2520*%2520%2540property-read%2520string%257Cnull%2520%2524greeting%2520Greeting.%2520Default%2520-%2520%2522Hello%2522%250A%2520*%2520%2540property-read%2520string%257Cnull%2520%2524friend%2520%2520%2520Friend%2520name%250A%2520*%252F%250Aclass%2520GreetRequest%2520extends%2520%255CIlluminate%255CFoundation%255CHttp%255CFormRequest%250A%257B%250A%2520%2520%2520%2520public%2520function%2520rules%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%255B%255D%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <p>Далее, открываем страницу плагина в админке сайта и видим следующую картину:</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/6a/5b/6a5b0d42-f7f3-4074-a98e-e333f7c2a96a.png" width="1080" />
  </figure>
  <p>Как видно из скриншота, информация о роуте заполнилась следующим образом:</p>
  <ul>
    <li>HTTP-метод и URI роута - из самого описания роута в файле <code>route.php</code></li>
    <li>комментарий - из докблока к обработчику роута</li>
    <li>тип и комментарий к параметру <code>name</code> из него же</li>
    <li>параметры запроса с описанием типов и комментариями - из докблока к классу запроса</li>
  </ul>
  <p>Также, здесь можно ввести значения параметров, добавить заголовки и выполнить тестовые запросы (пока без файлов и массивов, возможно, они будут добавлены в будущем).</p>
  <p>Вот, собственно, и всё.</p>
  <h2>Вместо заключения</h2>
  <p>Данный плагин - не замена Postman или Swagger и он точно не предназначен для публичных API. Это скорее аналог <code>php artisan route:list</code> для тех, кому лень идти в терминал.</p>
  <p></p>
  <p>Скачать плагин:</p>
  <ul>
    <li><a href="https://github.com/gromitsoft/oc-routesbrowser-plugin" target="_blank">GitHub</a></li>
    <li><a href="https://packagist.org/packages/gromit/oc-routesbrowser-plugin" target="_blank">Packagist</a></li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/HSzRHt7fq6g</guid><link>https://blog.grom-it.ru/HSzRHt7fq6g?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/HSzRHt7fq6g?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>DadataWidgets plugin for OctoberCMS v.2</title><pubDate>Thu, 13 May 2021 17:37:06 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/af/21/af219ac1-6579-44bc-aaa9-ceb734c044aa.jpeg"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>dadata</tt:hashtag><tt:hashtag>widgets</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/a6/76/a676802a-a1a1-4663-8b5c-82f3aa2c9d12.jpeg"></img>Плагин интеграции подсказок сервиса Dadata в OctoberCMS v.2]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/a6/76/a676802a-a1a1-4663-8b5c-82f3aa2c9d12.jpeg" width="1200" />
  </figure>
  <p>Плагин интеграции подсказок сервиса Dadata в OctoberCMS v.2</p>
  <h2>Установка</h2>
  <pre>composer require gromit/oc-dadatawidgets-plugin</pre>
  <h2>Настройка</h2>
  <p>Для использования плагина, необходимо получить токен сервиса Dadata. Получить токен можно бесплатно - необходимо зарегистрироваться на сайте <a href="https://dadata.ru/" target="_blank">https://dadata.ru/</a> и скопировать его в профиле.</p>
  <p>Полученный токен (он же АПИ КЛЮЧ) вставляем в настройки плагина через <strong>Настройки -&gt; Dadata Widgets</strong></p>
  <h2>Использование</h2>
  <p>В конфиге формы указываем тип поля <strong>dadataSuggestions</strong>, тип подсказки <strong>suggestion</strong> и привязку данных к полям формы.</p>
  <p><strong>Пример конфига поля:</strong></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=name%253A%250A%2520%2520%2520%2520label%253A%2520%25D0%259D%25D0%25B0%25D0%25B7%25D0%25B2%25D0%25B0%25D0%25BD%25D0%25B8%25D0%25B5%2520%25D0%25BA%25D0%25BE%25D0%25BC%25D0%25BF%25D0%25B0%25D0%25BD%25D0%25B8%25D0%25B8%250A%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520suggestion%253A%2520company"></iframe>
  </figure>
  <h2>Маппер</h2>
  <p>В конфиге поля можно указать соответствие данных, получаемых из сервиса Dadata, и полей формы, куда эти данные вставить.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=map%253A%250A%2520%2520%2520%2520%25D0%25B8%25D0%25BC%25D1%258F%2520%25D0%25BF%25D0%25BE%25D0%25BB%25D1%258F1%253A%2520%25D0%25B7%25D0%25BD%25D0%25B0%25D1%2587%25D0%25B5%25D0%25BD%25D0%25B8%25D0%25B5%2520%25D0%25B8%25D0%25B7%2520%25D0%25BE%25D1%2582%25D0%25B2%25D0%25B5%25D1%2582%25D0%25B0%2520Dadata%250A%2520%2520%2520%2520%25D0%25B8%25D0%25BC%25D1%258F%2520%25D0%25BF%25D0%25BE%25D0%25BB%25D1%258F2%253A%2520%25D0%25B7%25D0%25BD%25D0%25B0%25D1%2587%25D0%25B5%25D0%25BD%25D0%25B8%25D0%25B5%2520%25D0%25B8%25D0%25B7%2520%25D0%25BE%25D1%2582%25D0%25B2%25D0%25B5%25D1%2582%25D0%25B0%2520Dadata%250A%2520%2520%2520%2520...%250A%2520%2520%2520%2520%25D0%25B8%25D0%25BC%25D1%258F%2520%25D0%25BF%25D0%25BE%25D0%25BB%25D1%258FN%253A%2520%25D0%25B7%25D0%25BD%25D0%25B0%25D1%2587%25D0%25B5%25D0%25BD%25D0%25B8%25D0%25B5%2520%25D0%25B8%25D0%25B7%2520%25D0%25BE%25D1%2582%25D0%25B2%25D0%25B5%25D1%2582%25D0%25B0%2520Dadata"></iframe>
  </figure>
  <p>Структура возвращаемых данных сервиса Dadata зависит от типа подсказки:</p>
  <p>suggestion: <strong>company -&gt; </strong><a href="https://dadata.ru/api/suggest/party/#response" target="_blank">https://dadata.ru/api/suggest/party/#response</a></p>
  <p>suggestion: <strong>bank -&gt; </strong><a href="https://dadata.ru/api/suggest/bank/#response" target="_blank">https://dadata.ru/api/suggest/bank/#response</a></p>
  <p>suggestion: <strong>address -&gt; </strong><a href="https://dadata.ru/api/suggest/address/#response" target="_blank">https://dadata.ru/api/suggest/address/#response</a></p>
  <p>suggestion: <strong>email -&gt; </strong><a href="https://dadata.ru/api/suggest/email/#response" target="_blank">https://dadata.ru/api/suggest/email/#response</a></p>
  <p>suggestion: <strong>fio -&gt; </strong><a href="https://dadata.ru/api/suggest/name/#response" target="_blank">https://dadata.ru/api/suggest/name/#response</a></p>
  <p>Соответственно в маппере используем описанные выше структуры данных.</p>
  <h2>Примеры</h2>
  <p>Небольшие примеры использования виджета для разных ситуаций</p>
  <h3>Поиск контрагента и заполнение нужных полей</h3>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=fields%253A%250A%2520%2520%2520%2520name%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259D%25D0%25B0%25D0%25B7%25D0%25B2%25D0%25B0%25D0%25BD%25D0%25B8%25D0%25B5%2520%25D0%25BA%25D0%25BE%25D0%25BC%25D0%25BF%25D0%25B0%25D0%25BD%25D0%25B8%25D0%25B8%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520%2520%2520%2520%2520suggestion%253A%2520company%250A%2520%2520%2520%2520%2520%2520%2520%2520map%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520name%253A%2520value%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520inn%253A%2520data.inn%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520kpp%253A%2520data.kpp%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520ogrn%253A%2520data.ogrn%250A%2520%2520%2520%2520inn%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2598%25D0%259D%25D0%259D%250A%2520%2520%2520%2520kpp%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259A%25D0%259F%25D0%259F%250A%2520%2520%2520%2520ogrn%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259E%25D0%2593%25D0%25A0%25D0%259D"></iframe>
  </figure>
  <h3>Поиск банка</h3>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=fields%253A%250A%2520%2520%2520%2520bank%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2591%25D0%25B0%25D0%25BD%25D0%25BA%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520%2520%2520%2520%2520suggestion%253A%2520bank%250A%2520%2520%2520%2520%2520%2520%2520%2520map%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520bank%253A%2520value%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520bic%253A%2520data.bic%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520cs%253A%2520data.correspondent_account%250A%2520%2520%2520%2520bic%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2591%25D0%2598%25D0%259A%250A%2520%2520%2520%2520cs%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259A%252F%25D0%25A1"></iframe>
  </figure>
  <h3>Ввод адреса и его последующий разбор</h3>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=fields%253A%250A%2520%2520%2520%2520address%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2590%25D0%25B4%25D1%2580%25D0%25B5%25D1%2581%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520%2520%2520%2520%2520suggestion%253A%2520address%250A%2520%2520%2520%2520%2520%2520%2520%2520map%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520address%253A%2520value%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520country%253A%2520data.country%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520city%253A%2520data.city%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520lat%253A%2520data.geo_lat%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520lon%253A%2520data.geo_lon%250A%250A%2520%2520%2520%2520country%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%25A1%25D1%2582%25D1%2580%25D0%25B0%25D0%25BD%25D0%25B0%250A%2520%2520%2520%2520city%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2593%25D0%25BE%25D1%2580%25D0%25BE%25D0%25B4%250A%2520%2520%2520%2520lat%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259A%25D0%25BE%25D0%25BE%25D1%2580%25D0%25B4%25D0%25B8%25D0%25BD%25D0%25B0%25D1%2582%25D1%258B%2520%28%25D1%2588%25D0%25B8%25D1%2580%25D0%25BE%25D1%2582%25D0%25B0%29%250A%2520%2520%2520%2520lon%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259A%25D0%25BE%25D0%25BE%25D1%2580%25D0%25B4%25D0%25B8%25D0%25BD%25D0%25B0%25D1%2582%25D1%258B%2520%28%25D0%25B4%25D0%25BE%25D0%25BB%25D0%25B3%25D0%25BE%25D1%2582%25D0%25B0%29"></iframe>
  </figure>
  <h3>Помощь при вводе email-адреса и его разбор</h3>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=fields%253A%250A%2520%2520%2520%2520email%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Email%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520%2520%2520%2520%2520suggestion%253A%2520email%250A%2520%2520%2520%2520%2520%2520%2520%2520map%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520email%253A%2520value%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520local%253A%2520data.local%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520domain%253A%2520data.domain%250A%2520%2520%2520%2520local%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259B%25D0%25BE%25D0%25BA%25D0%25B0%25D0%25BB%25D1%258C%25D0%25BD%25D0%25BE%25D0%25B5%2520%28%25D0%25B4%25D0%25BE%2520%25D1%2581%25D0%25BE%25D0%25B1%25D0%25B0%25D1%2587%25D0%25BA%25D0%25B8%29%250A%2520%2520%2520%2520domain%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2594%25D0%25BE%25D0%25BC%25D0%25B5%25D0%25BD"></iframe>
  </figure>
  <h3>Помощь при вводе ФИО и разбор</h3>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=fields%253A%250A%2520%2520%2520%2520fio%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%25A4%25D0%2598%25D0%259E%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520dadataSuggestions%250A%2520%2520%2520%2520%2520%2520%2520%2520suggestion%253A%2520fio%250A%2520%2520%2520%2520%2520%2520%2520%2520map%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520fio%253A%2520value%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520surname%253A%2520data.surname%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520name%253A%2520data.name%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520patronymic%253A%2520data.patronymic%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520gender%253A%2520data.gender%250A%2520%2520%2520%2520surname%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%25A4%25D0%25B0%25D0%25BC%25D0%25B8%25D0%25BB%25D0%25B8%25D1%258F%250A%2520%2520%2520%2520name%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%2598%25D0%25BC%25D1%258F%250A%2520%2520%2520%2520patronymic%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259E%25D1%2582%25D1%2587%25D0%25B5%25D1%2582%25D1%2581%25D1%2582%25D0%25B2%25D0%25BE%250A%2520%2520%2520%2520gender%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520%25D0%259F%25D0%25BE%25D0%25BB"></iframe>
  </figure>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="dadata">#dadata</tt-tag>
    <tt-tag name="widgets">#widgets</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/eBj_dft11WY</guid><link>https://blog.grom-it.ru/eBj_dft11WY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/eBj_dft11WY?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Разработка простейшего виджета формы с использованием Vue.js для OctoberCMS v.2</title><pubDate>Tue, 11 May 2021 18:20:12 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/59/db/59db1f28-0a86-4fe9-a275-9b889b7b2150.jpeg"></media:content><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>vuejs</tt:hashtag><tt:hashtag>formwidgets</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/c3/f3/c3f37965-ee1f-471b-8e50-ba4a71ecdc97.jpeg"></img>С появлением поддержки Vue.js в новом October, открылись новые возможности по созданию виджетов и прочих интересных вещей в админке нашей любимой CMS.]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/c3/f3/c3f37965-ee1f-471b-8e50-ba4a71ecdc97.jpeg" width="1200" />
  </figure>
  <p>С появлением поддержки Vue.js в новом October, открылись новые возможности по созданию виджетов и прочих интересных вещей в админке нашей любимой CMS.</p>
  <p>Так как документации по поводу использования Vue.js в настоящий момент еще нет и автор не претендует на звание Кунг-Vue мастера, то публикуем этот гайд исходя из опыта, полученного в процессе изучения исходников новой системы.</p>
  <h3>Задача</h3>
  <p>Задача простая, бесполезная, но сгодится для примера - это создание formWidget&#x27;а для получения адреса, с использованием Яндекс.Карт</p>
  <h3>Подготовка</h3>
  <p>Перед тем как начать - создадим тестовый плагин, назовем его Address.<br />Данный материал предполагает, что читатель знаком с базовыми командами по созданию плагинов, моделей и пр. в OctoberCMS.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%25D0%25A1%25D0%25BE%25D0%25B7%25D0%25B4%25D0%25B0%25D0%25B4%25D0%25B8%25D0%25BC%2520%25D0%25BF%25D0%25BB%25D0%25B0%25D0%25B3%25D0%25B8%25D0%25BD%2520%25D0%25B4%25D0%25BB%25D1%258F%2520%25D1%258D%25D0%25BA%25D1%2581%25D0%25BF%25D0%25B5%25D1%2580%25D0%25B8%25D0%25BC%25D0%25B5%25D0%25BD%25D1%2582%25D0%25BE%25D0%25B2%252C%2520%25D0%25BD%25D0%25B0%25D0%25B7%25D0%25BE%25D0%25B2%25D0%25B5%25D0%25BC%2520%25D0%25B5%25D0%25B3%25D0%25BE%2520Sandbox%250A%250Aphp%2520artisan%2520create%253Aplugin%2520sandbox.address%250A%2520%2520%250A%252F%252F%2520%25D0%25A1%25D0%25BE%25D0%25B7%25D0%25B4%25D0%25B0%25D0%25B4%25D0%25B8%25D0%25BC%2520%25D0%25BC%25D0%25BE%25D0%25B4%25D0%25B5%25D0%25BB%25D1%258C%2520%25D0%25B8%2520%25D0%25BA%25D0%25BE%25D0%25BD%25D1%2582%25D1%2580%25D0%25BE%25D0%25BB%25D0%25BB%25D0%25B5%25D1%2580%2520%25D0%25B4%25D0%25BB%25D1%258F%2520%25D0%25BF%25D0%25BB%25D0%25B0%25D0%25B3%25D0%25B8%25D0%25BD%25D0%25B0%250Aphp%2520artisan%2520create%253Amodel%2520sandbox.address%2520address%250Aphp%2520artisan%2520create%253Acontroller%2520sandbox.address%2520addresses%250A"></iframe>
  </figure>
  <p>Далее ускоренный прогон - миграция, модель, контроллер.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%25D0%259C%25D0%25B8%25D0%25B3%25D1%2580%25D0%25B0%25D1%2586%25D0%25B8%25D1%258F%2520create_addresses_table.php%250A%250A%253C%253Fphp%2520namespace%2520Sandbox%255CAddress%255CUpdates%253B%250A%250Ause%2520Schema%253B%250Ause%2520October%255CRain%255CDatabase%255CSchema%255CBlueprint%253B%250Ause%2520October%255CRain%255CDatabase%255CUpdates%255CMigration%253B%250A%250Aclass%2520CreateAddressesTable%2520extends%2520Migration%250A%257B%250A%2520%2520%2520%2520public%2520function%2520up%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520Schema%253A%253Acreate%28%27sandbox_address_addresses%27%252C%2520function%2520%28Blueprint%2520%2524table%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2524table-%253Eengine%2520%253D%2520%27InnoDB%27%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2524table-%253Eincrements%28%27id%27%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2524table-%253Estring%28%27address%27%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2524table-%253Etimestamps%28%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%29%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520public%2520function%2520down%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520Schema%253A%253AdropIfExists%28%27sandbox_address_addresses%27%29%253B%250A%2520%2520%2520%2520%257D%250A%257D%250A%250A%252F%252F%2520%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%250A%252F%252F%2520%25D0%259C%25D0%25BE%25D0%25B4%25D0%25B5%25D0%25BB%25D1%258C%2520Address%250A%250A%253C%253Fphp%2520namespace%2520Sandbox%255CAddress%255CModels%253B%250A%250Ause%2520Model%253B%250A%250A%252F**%250A%2520*%2520Address%2520Model%250A%2520*%252F%250Aclass%2520Address%2520extends%2520Model%250A%257B%250A%2520%2520%2520%2520%252F**%250A%2520%2520%2520%2520%2520*%2520%2540var%2520string%2520table%2520associated%2520with%2520the%2520model%250A%2520%2520%2520%2520%2520*%252F%250A%2520%2520%2520%2520public%2520%2524table%2520%253D%2520%27sandbox_address_addresses%27%253B%250A%257D%250A%250A%252F%252F%2520%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%250A%252F%252F%2520%25D0%259A%25D0%25BE%25D0%25BD%25D1%2582%25D1%2580%25D0%25BE%25D0%25BB%25D0%25BB%25D0%25B5%25D1%2580%2520Addresses%250A%250A%253C%253Fphp%2520namespace%2520Sandbox%255CAddress%255CControllers%253B%250A%250Ause%2520BackendMenu%253B%250Ause%2520Backend%255CBehaviors%255CFormController%253B%250Ause%2520Backend%255CBehaviors%255CListController%253B%250Ause%2520Backend%255CClasses%255CController%253B%250A%250A%252F**%250A%2520*%2520Addresses%2520Back-end%2520Controller%250A%2520*%252F%250Aclass%2520Addresses%2520extends%2520Controller%250A%257B%250A%2520%2520%2520%2520public%2520%2524implement%2520%253D%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520FormController%253A%253Aclass%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520ListController%253A%253Aclass%250A%2520%2520%2520%2520%255D%253B%250A%250A%2520%2520%2520%2520public%2520%2524formConfig%2520%253D%2520%27config_form.yaml%27%253B%250A%2520%2520%2520%2520public%2520%2524listConfig%2520%253D%2520%27config_list.yaml%27%253B%250A%250A%2520%2520%2520%2520public%2520function%2520__construct%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520parent%253A%253A__construct%28%29%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520BackendMenu%253A%253AsetContext%28%27Sandbox.Address%27%252C%2520%27address%27%252C%2520%27addresses%27%29%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <p>Не забываем обновить файл <code>updates/version.yaml</code> </p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=1.0.1%253A%2520%250A%2520%2520%2520%2520-%2520First%2520version%2520of%2520address%250A%2520%2520%2520%2520-%2520create_addresses_table.php"></iframe>
  </figure>
  <p>и выполнить:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=application%2Fx-sh&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=php%2520artisan%2520plugin%253Arefresh%2520sandbox.address"></iframe>
  </figure>
  <p>Не стану описывать, что и как - мы только что создали простой скелет, для того, чтобы было куда &quot;прицепить&quot; наш formWidget.</p>
  <h2>Создание виджета</h2>
  <p>Пробежимся по созданию виджета - следуя документации. Назовем наш виджет <code>MapPicker</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=application%2Fx-sh&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=php%2520artisan%2520create%253Aformwidget%2520sandbox.address%2520MapPicker"></iframe>
  </figure>
  <p>В итоге у нас будет создан шаблон нашего виджета, с ним и будем работать.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/f3/6b/f36b77e8-2f25-4f4d-9ea6-c17752eed7fc.png" width="372" />
  </figure>
  <p>Сразу зарегистрируем виджет (согласно документации) в файле Plugin.php нашего плагина SandBox:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%250Apublic%2520function%2520registerFormWidgets%28%29%250A%257B%250A%2520%2520%2520%2520return%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520Sandbox%255CAddress%255CFormWidgets%255CMapPicker%253A%253Aclass%2520%253D%253E%2520%27mapPicker%27%252C%250A%2520%2520%2520%2520%255D%253B%250A%257D%250A"></iframe>
  </figure>
  <p>И добавим поле address с типом mapPicker в yaml-файл <code>sandbox/address/models/address/fields.yaml</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%2523%2520%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%250A%2523%2520%2520Form%2520Field%2520Definitions%250A%2523%2520%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%253D%250A%250Afields%253A%250A%2520%2520%2520%2520id%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520ID%250A%2520%2520%2520%2520%2520%2520%2520%2520disabled%253A%2520true%250A%2520%2520%2520%2520address%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520MapPicker%250A%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520mapPicker%250A"></iframe>
  </figure>
  <p>Теперь в браузере перейдем на страницу /backend/sandbox/address/addresses/create и увидим стандартный сгенерированный шаблон виджета. С ним и будем работать.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/bc/6b/bc6ba12a-1210-43a4-99f8-3b78263a5c83.png" width="1776" />
  </figure>
  <p>Итак, что мы хотим здесь видеть:</p>
  <ol>
    <li>Поле ввода адреса</li>
    <li>Кнопку, которая откроет в модальном окне карту</li>
    <li>Собственно карту, при клике по которой, мы получим адрес и вставим его в поле ввода</li>
  </ol>
  <h3>Поле и кнопка</h3>
  <p>Поправим стандартный шаблон партиалки, добавив в него кнопку и немного css.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520sandbox%252Faddress%252Fformwidgets%252Fmappicker%252Fpartials%252F_mappicker.htm%250A%250A%253C%253Fphp%2520if%2520%28%2524this-%253EpreviewMode%29%253A%2520%253F%253E%250A%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522form-control%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%253F%253D%2520%2524value%2520%253F%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%250A%253C%253Fphp%2520else%253A%2520%253F%253E%250A%250A%2520%2520%2520%2520%253Cdiv%2520id%253D%2522widget-%253C%253F%253D%2520%2524this-%253EgetId%28%27input%27%29%2520%253F%253E%2522%2520class%253D%2522mappicker-widget%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cinput%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253D%2522text%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520id%253D%2522%253C%253F%253D%2520%2524this-%253EgetId%28%27input%27%29%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520name%253D%2522%253C%253F%253D%2520%2524name%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520value%253D%2522%253C%253F%253D%2520%2524value%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520class%253D%2522form-control%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520autocomplete%253D%2522off%2522%252F%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cbutton%2520type%253D%2522button%2522%2520class%253D%2522btn%2520btn-primary%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%25D0%259A%25D0%25B0%25D1%2580%25D1%2582%25D0%25B0%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fbutton%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%250A%253C%253Fphp%2520endif%2520%253F%253E%250A"></iframe>
  </figure>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=css&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F*%250A%2520*%2520sandbox%252Faddress%252Fformwidgets%252Fmappicker%252Fassets%252Fcss%252Fmappicker.css%250A%2520*%252F%250A%250A.mappicker-widget%257B%250A%2520%2520%2520%2520display%253A%2520flex%253B%250A%2520%2520%2520%2520gap%253A%252015px%253B%250A%257D%250A"></iframe>
  </figure>
  <p>Получили примерно то, что надо =)</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/37/bf/37bf6314-6fbd-4bdd-9a4a-bfd25d2143a0.png" width="1778" />
  </figure>
  <p>Далее используем стандартный компонент &lt;backend-component-modal&gt;, немного покопавшись в исходниках модуля editor и подглядев, как его вызывать.</p>
  <p>Немного модифицируем нашу партиалку:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520sandbox%252Faddress%252Fformwidgets%252Fmappicker%252Fpartials%252F_mappicker.htm%250A%250A%253C%253Fphp%2520if%2520%28%2524this-%253EpreviewMode%29%253A%2520%253F%253E%250A%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522form-control%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%253F%253D%2520%2524value%2520%253F%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%250A%253C%253Fphp%2520else%253A%2520%253F%253E%250A%250A%2520%2520%2520%2520%253Cdiv%2520id%253D%2522widget-%253C%253F%253D%2520%2524this-%253EgetId%28%27input%27%29%2520%253F%253E%2522%2520class%253D%2522mappicker-widget%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cinput%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253D%2522text%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520id%253D%2522%253C%253F%253D%2520%2524this-%253EgetId%28%27input%27%29%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520name%253D%2522%253C%253F%253D%2520%2524name%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520value%253D%2522%253C%253F%253D%2520%2524value%2520%253F%253E%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520class%253D%2522form-control%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520autocomplete%253D%2522off%2522%252F%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cbutton%2520type%253D%2522button%2522%2520class%253D%2522btn%2520btn-primary%2522%2520%2540click%253D%2522%2524refs.modal.show%28%29%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%25D0%259A%25D0%25B0%25D1%2580%25D1%2582%25D0%25B0%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fbutton%253E%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cbackend-component-modal%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520ref%253D%2522modal%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Aaria-labeled-by%253D%2522modalTitleId%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Aunique-key%253D%2522uniqueKey%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520size%253D%2522large%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Adraggable%253D%2522false%2522%250A%2520%2520%2520%2520%2520%2520%2520%2520%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Ctemplate%2520v-slot%253Acontent%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522modal-header%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Ch4%2520class%253D%2522modal-title%2522%253E%25D0%2597%25D0%25B0%25D0%25B3%25D0%25BE%25D0%25BB%25D0%25BE%25D0%25B2%25D0%25BE%25D0%25BA%253C%252Fh4%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522modal-body%2522%253E%25D0%25A2%25D0%25B5%25D0%25BA%25D1%2581%25D1%2582%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Ftemplate%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fbackend-component-modal%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%250A%2520%2520%2520%2520%253Cscript%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520document.addEventListener%28%27DOMContentLoaded%27%252C%2520function%2520%28%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520new%2520Vue%28%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520el%253A%2520%27%2523widget-%253C%253F%253D%2520%2524this-%253EgetId%28%27input%27%29%2520%253F%253E%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520data%253A%2520function%2520%28%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520uniqueKey%253A%2520%2524.oc.domIdManager.generate%28%27modal-mappicker%27%29%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520modalTitleId%253A%2520%2524.oc.domIdManager.generate%28%27modal-mappicker-title%27%29%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257D%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257D%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%29%253B%250A%2520%2520%2520%2520%253C%252Fscript%253E%250A%250A%253C%253Fphp%2520endif%2520%253F%253E%250A%250A"></iframe>
  </figure>
  <p>Проверяем:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/ae/41/ae41d9f3-ca94-4673-9342-52bfba9532f4.png" width="1756" />
  </figure>
  <p>Ура, получилось! Дело за малым, наполнить модалку картой и функционалом.</p>
  <h3>Яндекс.Карты</h3>
  <p>Достаточно просто и красиво всё описано тут - <a href="https://vue-yandex-maps.github.io/" target="_blank">https://vue-yandex-maps.github.io/</a></p>
  <p>Немного модифицируем файл виджета, добавив в метод загрузки ассетов одну строку</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520sandbox%252Faddress%252Fformwidgets%252FMapPicker.php%250A%250A%250Apublic%2520function%2520loadAssets%28%29%250A%257B%250A%2520%2520%2520%2520%2524this-%253EaddCss%28%27css%252Fmappicker.css%27%252C%2520%27sandbox.address%27%29%253B%250A%2520%2520%2520%2520%2524this-%253EaddJs%28%27js%252Fmappicker.js%27%252C%2520%27sandbox.address%27%29%253B%250A%2520%2520%2520%2520%2524this-%253EaddJs%28%27https%253A%252F%252Funpkg.com%252Fvue-yandex-maps%27%252C%2520%27sandbox.address%27%29%253B%250A%257D%250A%250A%250A"></iframe>
  </figure>
  <p>Ну вот и всё - согласно документации, мы подключили плагин карт напрямую, и теперь дело техники, подпилить нашу партиалку для работы с ним.</p>
  <p>Долго описывать не будем - покажем результат модифицированной партиалки</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/47/8d/478dd092-cece-4090-96c7-4cb16902db5c.png" width="1818" />
  </figure>
  <p>И немного докинем стилей в css</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520sandbox%252Faddress%252Fformwidgets%252Fmappicker%252Fassets%252Fcss%252Fmappicker.css%250A%250A%252F*%250A%2520*%2520This%2520is%2520a%2520sample%2520StyleSheet%2520file%2520used%2520by%2520MapPicker%250A%2520*%250A%2520*%2520You%2520can%2520delete%2520this%2520file%2520if%2520you%2520want%250A%2520*%252F%250A.mappicker-widget%2520%257B%250A%2520%2520%2520%2520display%253A%2520flex%253B%250A%2520%2520%2520%2520gap%253A%252015px%253B%250A%257D%250A%250A%2523yandex-map%2520%257B%250A%2520%2520%2520%2520height%253A%2520400px%253B%250A%2520%2520%2520%2520margin-bottom%253A%252020px%253B%250A%257D%250A%250A.ymap-container%2520%257B%250A%2520%2520%2520%2520height%253A%2520100%2525%253B%250A%257D%250A%250A%250A"></iframe>
  </figure>
  <h3>Результат</h3>
  <p>Собственно, чтобы не придумывать велосипед - небольшое видео-демонстрация</p>
  <figure class="m_column">
    <iframe src="https://www.youtube.com/embed/Q7og6lPstnc?autoplay=0&loop=0&mute=0"></iframe>
  </figure>
  <p>Исходники виджета можно скачать тут <a href="https://github.com/gromitsoft/sandbox-formwidget" target="_blank">https://github.com/gromitsoft/sandbox-formwidget</a></p>
  <p></p>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="vuejs">#vuejs</tt-tag>
    <tt-tag name="formwidgets">#formwidgets</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/ZimADT2FeOx</guid><link>https://blog.grom-it.ru/ZimADT2FeOx?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/ZimADT2FeOx?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Особенности разработки REST API на OctoberCMS</title><pubDate>Sun, 09 May 2021 14:18:32 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/07/84/0784e2a3-9fda-460c-be65-9817e07d99b6.jpeg"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>restapi</tt:hashtag><tt:hashtag>cors</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/46/b5/46b5a675-f67a-43d4-b0b7-95b746f9be91.jpeg"></img>В данном уроке мы рассмотрим разработку REST API на основе OctoberCMS.]]></description><content:encoded><![CDATA[
  <figure class="m_original">
    <img src="https://teletype.in/files/46/b5/46b5a675-f67a-43d4-b0b7-95b746f9be91.jpeg" width="1200" />
  </figure>
  <p>В данном уроке мы рассмотрим разработку REST API на основе OctoberCMS.</p>
  <p>Для чего это может понадобиться? Например, для создания роутов для последующего обращения к ним через AJAX. </p>
  <p>Тут читатель может возразить, что в October для этого есть встроенный <br />AJAX-фреймворк, работающий с компонентами и методами, находящимися прямо в файле страницы и будет прав. Но в некоторых случаях встроенный <br />AJAX-фреймворк может быть не удобен или совсем не подходить. Особенно, при внедрении сторонних библиотек с динамической подгрузкой данных, таких как Select2.</p>
  <p>Также, как показала практика, OctoberCMS отлично подходит как основа для headless решений, предоставляющих REST API для SPA или мобильных приложений, с удобной админкой в комплекте.</p>
  <h3>Приступая к работе</h3>
  <p>Как известно, OctoberCMS основан на Laravel. Из этого следует, что разработка REST API на OctoberCMS будет очень похожа на разработку REST API на Laravel.</p>
  <p>Данный урок подразумевает, что Вы уже умеете создавать плагины для OctoberCMS. Также нам понадобится развернутый проект и немного свободного времени.</p>
  <h3>Начало</h3>
  <p>Для начала необходимо создать плагин. Для этого, как обычно, пойдем в консоль и попросим <code>artisan</code> создать нам пустой плагин.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=application%2Fx-sh&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%2524%2520php%2520artisan%2520create%253Aplugin%2520Sandbox.RestApi"></iframe>
  </figure>
  <p>Или создадим всё вручную (или даже через Builder), кому как больше нравится.</p>
  <p>Далее, для объявления роутов нашего REST API создадим файл <code>/plugins/sandbox/restapi/routes.php</code> и объявим в нем наш первый роут.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Froutes.php%250A%250A%253C%253Fphp%250A%250Ause%2520Illuminate%255CSupport%255CFacades%255CRoute%253B%250A%250ARoute%253A%253Aprefix%28%27api%27%29-%253Egroup%28function%2520%28%29%2520%257B%250A%2520%2520%2520%2520Route%253A%253Aget%28%27hello%27%252C%2520function%2520%28%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520response%28%27hello%2520rest%2520api%27%29%253B%250A%2520%2520%2520%2520%257D%29%253B%250A%257D%29%253B"></iframe>
  </figure>
  <p>Теперь, если обратиться к этому роуту, то мы увидим ответ - <strong>hello rest api</strong>. Для этого мы обычно используем <a href="https://www.postman.com/" target="_blank">Postman</a>.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/ac/a8/aca80026-ed35-4389-8264-135c25cc0ac3.png" width="1262" />
  </figure>
  <p>Далее - все как и в случае с Laravel. Только папку для контроллеров нам придется создать самостоятельно. Создадим наш контроллер, например, в файле <code>/plugins/sandbox/restapi/http/controllers/HelloController.php</code>, и добавим роут для него:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Fhttp%252Fcontrollers%252FHelloController.php%250A%250A%253C%253Fphp%250A%250Anamespace%2520Sandbox%255CRestapi%255CHttp%255CControllers%253B%250A%250Ause%2520Illuminate%255CHttp%255CJsonResponse%253B%250Ause%2520Illuminate%255CRouting%255CController%253B%250A%250Aclass%2520HelloController%2520extends%2520Controller%250A%257B%250A%2520%2520%2520%2520public%2520function%2520greet%28string%2520%2524name%29%253A%2520JsonResponse%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520response%28%29-%253Ejson%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27greet%27%2520%253D%253E%2520%2522Hello%2520%2524name%2522%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%29%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Froutes.php%250A%250A%253C%253Fphp%250A%250Ause%2520Illuminate%255CSupport%255CFacades%255CResponse%253B%250Ause%2520Illuminate%255CSupport%255CFacades%255CRoute%253B%250Ause%2520Sandbox%255CRestApi%255CHttp%255CControllers%255CHelloController%253B%250A%250ARoute%253A%253Aprefix%28%27api%27%29-%253Egroup%28function%2520%28%29%2520%257B%250A%2520%2520%2520%2520Route%253A%253Aget%28%27hello%252F%257Bname%257D%27%252C%2520%255BHelloController%253A%253Aclass%252C%2520%27greet%27%255D%29%253B%250A%257D%29%253B"></iframe>
  </figure>
  <p>Теперь обратимся к роуту <code>/api/hello/John%20Doe</code> и увидим, что наш API отвечает соответствующим образом.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/7d/e4/7de430a7-7372-4716-9dc0-6cc5bae62c1a.png" width="1262" />
  </figure>
  <p>Главное здесь - наследовать контроллеры от <code>Illuminate\Routing\Controller</code>, то есть от базового контроллера Laravel. А не от <code>Backend\Classes\Controller</code>, так как последний предназначен для создания страниц в админ-панели OctoberCMS и плохо подходит для нашей задачи.</p>
  <h3>Особенности</h3>
  <p>OctoberCMS - это, в первую очередь, система предназначенная для разработки сайтов с классическим роутингом и генерацией HTML-страниц на сервере, а не REST API, поэтому некоторые вещи для разработки REST API подготовлены не очень хорошо.</p>
  <p>Основные вопросы - обработка исключений и CORS.</p>
  <h3>Обработка исключений</h3>
  <p>В Laravel, если мы запросим роут страницы через браузер и в процессе обработки запроса будет выброшено исключение - мы увидим красивую страницу с кучей полезной информации. В случае же с XHR запросом - получим json с текстом ошибки, кодом и стеком вызовов. И происходит это автоматически, то есть стандартный обработчик ошибок в Laravel сам понимает, какой ответ нам нужен. В случае с OctoberCMS это не так.</p>
  <p>Чтобы в этом убедиться, создадим роут, который, просто выбросит ошибку валидации, а затем обратимся к этому роуту.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Froutes.php%250A%250A%253C%253Fphp%250A%250Ause%2520October%255CRain%255CException%255CValidationException%253B%250A%250ARoute%253A%253Aget%28%27%252Fexception%27%252C%2520function%2520%28%29%2520%257B%250A%2520%2520%2520%2520throw%2520new%2520ValidationException%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%27something%27%2520%253D%253E%2520%27wrong%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%27mode%27%2520%2520%2520%2520%2520%2520%253D%253E%2520%27error%27%252C%250A%2520%2520%2520%2520%255D%29%253B%250A%257D%29%253B"></iframe>
  </figure>
  <p>Откроем Postman и запросим наш роут, не забывая при этом добавить заголовок <code>X-Requested-With</code> со значением <code>XMLHttpRequest</code>, чтобы приложение понимало, что у нас тут AJAX-запрос и отправлять отрендеренную страницу не нужно.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/b7/ff/b7ff374e-7f09-415b-9304-8e8fe604616c.png" width="1345" />
  </figure>
  <p>Не густо! Что ж, значит нам придется сделать обработку исключений самим.</p>
  <p>Согласно документации, для этого надо в методе <code>boot</code> класса <code>Plugin</code> нашего плагина добавить конструкцию вида:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252FPlugin.php%250A%250A%253C%253Fphp%250A%250Anamespace%2520Sandbox%255CRestApi%253B%250A%250Ause%2520Config%253B%250Ause%2520Fruitcake%255CCors%255CHandleCors%253B%250Ause%2520Illuminate%255CContracts%255CHttp%255CKernel%253B%250A%250Aclass%2520Plugin%2520%257B%250A%250A%2520%2520%2520%2520%252F%252F%2520...%250A%250A%2520%2520%2520%2520public%2520function%2520boot%28%29%253A%2520void%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520App%253A%253Aerror%28function%2520%28ValidationException%2520%2524exception%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520response%28%29-%253Ejson%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27message%27%2520%253D%253E%2520%27validation%2520exception%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27errors%27%2520%2520%253D%253E%2520%2524exception-%253EgetErrors%28%29%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%255D%252C%2520422%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%29%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520%252F%252F%2520...%250A%250A%257D"></iframe>
  </figure>
  <p>Теперь попробуем обратиться к роуту еще раз.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/7c/54/7c54a01a-8804-4b04-b30c-a85a70679d0f.png" width="1345" />
  </figure>
  <p>Уже лучше. Но теперь появилась другая проблема: так как мы переопределили обработку для ошибок валидации, то у нас теперь не будет работать стандартная валидация в админке и на фронте.</p>
  <p>Для проверки этого утверждения, создадим пустой контроллер для админки, добавим в него метод <code>index</code> и бросим ошибку валидации.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Fcontrollers%252FHello.php%250A%250A%253C%253Fphp%250A%250Anamespace%2520Sandbox%255CRestapi%255CControllers%253B%250A%250Ause%2520Backend%255CClasses%255CController%253B%250Ause%2520October%255CRain%255CException%255CValidationException%253B%250A%250Aclass%2520Hello%2520extends%2520Controller%250A%257B%250A%2520%2520%2520%2520public%2520function%2520index%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520throw%2520new%2520ValidationException%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27something%27%2520%253D%253E%2520%27wrong%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27mode%27%2520%2520%2520%2520%2520%2520%253D%253E%2520%27error%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%29%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <p>Теперь откроем эту страницу в браузере.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/c3/6a/c36ac442-3414-4cd6-9210-921d08d4d7e9.png" width="1326" />
  </figure>
  <p>Да - это не то, что мы привыкли видеть в браузере, в случае ошибки.</p>
  <p>Для решения этой проблемы мы перенесем регистрацию хэндлера ошибок валидации в новый <code>middleware</code>.</p>
  <p>Создадим класс <code>ExceptionsMiddleware</code>, разместим его в <code>/plugins/sandbox/restapi/http/middleware/ExceptionsMiddleware.php</code> и добавим следующий код:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Fhttp%252Fmiddleware%252FExceptionsMiddleware.php%250A%250A%253C%253Fphp%250A%250Anamespace%2520Sandbox%255CRestapi%255CHttp%255CMiddleware%253B%250A%250Ause%2520App%253B%250Ause%2520Closure%253B%250Ause%2520Illuminate%255CHttp%255CRequest%253B%250Ause%2520October%255CRain%255CException%255CValidationException%253B%250A%250Aclass%2520ExceptionsMiddleware%250A%257B%250A%2520%2520%2520%2520public%2520function%2520handle%28Request%2520%2524request%252C%2520Closure%2520%2524next%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2524this-%253EregisterExceptionHandlers%28%29%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%2524next%28%2524request%29%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520private%2520function%2520registerExceptionHandlers%28%29%253A%2520void%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520App%253A%253Aerror%28function%2520%28ValidationException%2520%2524exception%29%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520response%28%29-%253Ejson%28%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27message%27%2520%253D%253E%2520%27validation%2520exception%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27errors%27%2520%2520%253D%253E%2520%2524exception-%253EgetErrors%28%29%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%255D%252C%2520422%29%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%29%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <p>Добавим наш новый <code>middleware</code> в файл <code>routes.php</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Froutes.php%250A%250A%253C%253Fphp%250A%250Ause%2520Sandbox%255CRestapi%255CHttp%255CMiddleware%255CExceptionsMiddleware%253B%250A%250ARoute%253A%253Agroup%28%255B%250A%2520%2520%2520%2520%27prefix%27%2520%2520%2520%2520%2520%253D%253E%2520%27api%27%252C%250A%2520%2520%2520%2520%27middleware%27%2520%253D%253E%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520ExceptionsMiddleware%253A%253Aclass%250A%2520%2520%2520%2520%255D%250A%255D%252C%2520function%2520%28%29%2520%257B%250A%2520%2520%2520%2520%252F%252F%2520%25D0%25BD%25D0%25B0%25D1%2588%25D0%25B8%2520%25D1%2580%25D0%25BE%25D1%2583%25D1%2582%25D1%258B%250A%257D%29%253B"></iframe>
  </figure>
  <p>Если же мы откроем страницу теперь, то увидим, привычную нам картину - роут с ошибкой будет отдавать все, что нам нужно.</p>
  <figure class="m_retina">
    <img src="https://teletype.in/files/fa/f2/faf2633a-2c38-4160-a48b-f3e2663f02bd.png" width="1345" />
  </figure>
  <h3>CORS</h3>
  <p>Следующий интересный вопрос - CORS.</p>
  <p>Если Вы не знаете, что такое CORS, то CORS (Cross-Origin Resource Sharing) - это механизм, позволяющий с помощью дополнительных заголовков запросов и ответов получать доступ к ресурсам других доменов. Подробнее можно почитать <a href="https://developer.mozilla.org/ru/docs/Web/HTTP/CORS" target="_blank">здесь</a>.</p>
  <p>В Laravel, начиная с 7-ой версии, есть встроенный middleware для работы с CORS. В OctoberCMS же его нет. А это значит, что этот вопрос нам придется решить самим.</p>
  <p>В Laravel встроенный middleware работает на основе пакета <strong>fruitcake/laravel-cors</strong>. Не будем изобретать велосипед и поступим так же.</p>
  <p>Про особенности работы с composer в OctoberCMS рекомендую ознакомиться с документацией. В нашем случае, будем считать, что разрабатываемый плагин предназначен исключительно для текущего проекта, а значит все зависимости будем добавлять в корневой composer.json.</p>
  <p>Идем в консоль и просим composer установить нам этот пакет.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=application%2Fx-sh&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=composer%2520req%2520fruitcake%252Flaravel-cors"></iframe>
  </figure>
  <p>Данный пакет требует конфига. Следуя рекомендациям из документации OctoberCMS создем конфиг не в общей папке проекта, а в папке плагина <code>/plugins/sandbox/restapi/config/cors.php</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F%252F%2520%252Fplugins%252Fsandbox%252Frestapi%252Fconfig%252Fcors.php%250A%250A%253C%253Fphp%250A%250Areturn%2520%255B%250A%250A%2520%2520%2520%2520%27paths%27%2520%253D%253E%2520%255B%27api%252F*%27%255D%252C%250A%250A%2520%2520%2520%2520%27allowed_methods%27%2520%253D%253E%2520%255B%27*%27%255D%252C%250A%250A%2520%2520%2520%2520%27allowed_origins%27%2520%253D%253E%2520%255B%27*%27%255D%252C%250A%250A%2520%2520%2520%2520%27allowed_origins_patterns%27%2520%253D%253E%2520%255B%255D%252C%250A%250A%2520%2520%2520%2520%27allowed_headers%27%2520%253D%253E%2520%255B%27*%27%255D%252C%250A%250A%2520%2520%2520%2520%27exposed_headers%27%2520%253D%253E%2520%255B%255D%252C%250A%250A%2520%2520%2520%2520%27max_age%27%2520%253D%253E%25200%252C%250A%250A%2520%2520%2520%2520%27supports_credentials%27%2520%253D%253E%2520true%252C%250A%250A%255D%253B"></iframe>
    <figcaption><em>Для чего нужен каждый параметр можно посмотреть <a href="https://github.com/fruitcake/laravel-cors#options" target="_blank">здесь</a>.</em></figcaption>
  </figure>
  <p>А затем регистрируем конфиг в методе <code>boot</code> класса <code>Plugin</code> нашего плагина, а также зарегистрируем <code>ServiceProvider</code> пакета и добавим глобальный <code>middleware</code>.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253C%253Fphp%250A%250Anamespace%2520Sandbox%255CRestApi%253B%250A%250Ause%2520Config%253B%250Ause%2520Fruitcake%255CCors%255CHandleCors%253B%250Ause%2520Illuminate%255CContracts%255CHttp%255CKernel%253B%250A%250Aclass%2520Plugin%2520%257B%250A%250A%2520%2520%2520%2520%252F%252F%2520...%250A%250A%2520%2520%2520%2520public%2520function%2520boot%28%29%253A%2520void%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520Config%253A%253Aset%28%27cors%27%252C%2520Config%253A%253Aget%28%27sandbox.restapi%253A%253Acors%27%29%29%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2524this-%253Eapp-%253Eregister%28CorsServiceProvider%253A%253Aclass%29%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2524this-%253Eapp%255BKernel%253A%253Aclass%255D-%253EpushMiddleware%28HandleCors%253A%253Aclass%29%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520%252F%252F%2520...%250A%250A%257D"></iframe>
  </figure>
  <h3>Заключение</h3>
  <p>Вещи описанные в статье, на самом деле, не обязательные, и разрабатывать REST API на OctoberCMS можно и без них, однако обработка исключений не плохо упрощает жизнь, а CORS - необходимая вещь, если вы разрабатываете API обращение к которому будет происходить с домена отличного от того, на котором это API находится.</p>
  <p>Код из статьи можно найти в <a href="https://github.com/gromitsoft/sandbox-restapi-plugin" target="_blank">репозитории</a></p>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="restapi">#restapi</tt-tag>
    <tt-tag name="cors">#cors</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/phbAYZqkAUt</guid><link>https://blog.grom-it.ru/phbAYZqkAUt?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/phbAYZqkAUt?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Изменение пользовательского меню в OctoberCMS</title><pubDate>Thu, 06 May 2021 14:29:03 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/d4/34/d434d1a0-8370-42f7-820d-44d3416819fe.jpeg"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>tips</tt:hashtag><tt:hashtag>tricks</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/fb/4c/fb4c9a0d-61d7-4d6f-adec-83faaf5792ca.jpeg"></img>При работе с OctoberCMS иногда возникает желание добавить свой пункт в выпадающее меню, которое появляется в верхнем правом углу при клике на иконку пользователя:]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/fb/4c/fb4c9a0d-61d7-4d6f-adec-83faaf5792ca.jpeg" width="1200" />
  </figure>
  <p>При работе с OctoberCMS иногда возникает желание добавить свой пункт в выпадающее меню, которое появляется в верхнем правом углу при клике на иконку пользователя:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/86/1d/861d4f88-29ad-40fd-8836-869de3d7ad24.png" width="873" />
  </figure>
  <p>Решение очень простое, для этого необходимо добавить пункты, которые вы хотите видеть в этом меню в метод <code>registerSettings()</code> вашего плагина.</p>
  <p>Пример добавления своего пункта:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%2520%2520%2520%2520public%2520function%2520registerSettings%28%29%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27settings%27%2520%253D%253E%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27label%27%2520%2520%2520%2520%2520%2520%2520%253D%253E%2520%27%25D0%2592%25D0%25B0%25D1%2588%25D0%25B0%2520%25D1%2581%25D1%2581%25D1%258B%25D0%25BB%25D0%25BA%25D0%25B0%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27description%27%2520%253D%253E%2520%27%25D0%2592%25D0%25B0%25D1%2588%2520%25D0%25B4%25D0%25B5%25D1%2581%25D0%25BA%25D1%2580%25D0%25B8%25D0%25BF%25D1%2588%25D0%25B5%25D0%25BD%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27icon%27%2520%2520%2520%2520%2520%2520%2520%2520%253D%253E%2520%27octo-icon-text-image%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27url%27%2520%2520%2520%2520%2520%2520%2520%2520%2520%253D%253E%2520%27https%253A%252F%252Fblog.grom-it.ru%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27context%27%2520%2520%2520%2520%2520%253D%253E%2520%27mysettings%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2509%27order%27%2509%2509%2520%2520%253D%253E%2520500%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%255D%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%253B%250A%2520%2520%2520%2520%257D%250A"></iframe>
  </figure>
  <p>То есть, необходимо всего лишь указать контекст для вашего пункта, и, чтобы он отобразился в выпадающем меню, значение контекста должно быть равно <code>mysettings</code>.</p>
  <p>Результат будет таким:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/02/40/0240d646-d2d2-4ae2-b15a-ea5680c9694b.png" width="879" />
  </figure>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="tips">#tips</tt-tag>
    <tt-tag name="tricks">#tricks</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/Xoyd0Fy3y</guid><link>https://blog.grom-it.ru/Xoyd0Fy3y?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/Xoyd0Fy3y?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Иконки в OctoberCMS 2.0</title><pubDate>Fri, 16 Apr 2021 13:03:52 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/bd/44/bd44d4ac-0380-49be-9aee-7dc8ac161e63.png"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>icons</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/06/91/069184d3-7c21-4919-be70-3540cb0a8196.png"></img>Свершилось, дождались =)]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/06/91/069184d3-7c21-4919-be70-3540cb0a8196.png" width="660" />
  </figure>
  <p>Свершилось, дождались =)</p>
  <p>Помимо косметики в бэке разработчики добавили, но пока не задокументировали новый набор иконок.</p>
  <p>Иконки вытащили и собрали в pdf, собирать в страницу лень =)</p>
  <p>Скачать можно тут - <a href="https://github.com/gromitsoft/sandbox/blob/master/new-icons.pdf" target="_blank">https://github.com/gromitsoft/sandbox/blob/master/new-icons.pdf</a></p>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="icons">#icons</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/B6gJn5cMH</guid><link>https://blog.grom-it.ru/B6gJn5cMH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/B6gJn5cMH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Строки в PHP и кавычки</title><pubDate>Sat, 03 Apr 2021 08:28:02 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/8b/66/8b668073-e311-4a2b-9130-252e1b048559.png"></media:content><category>php</category><tt:hashtag>php</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/45/5e/455efccf-769f-43a1-9f5f-09d334b4ca5c.png"></img>При изучении PHP многие обращают внимание на то, что строки можно обрамлять как одинарными, так и двойными кавычками. ]]></description><content:encoded><![CDATA[
  <p>При изучении PHP многие обращают внимание на то, что строки можно обрамлять как одинарными, так и двойными кавычками. </p>
  <p>Давайте разберемся - в чем разница.</p>
  <h3>Двойные кавычки VS Одинарные кавычки</h3>
  <figure class="m_column">
    <img src="https://teletype.in/files/45/5e/455efccf-769f-43a1-9f5f-09d334b4ca5c.png" width="724" />
  </figure>
  <p>Двойные кавычки позволяют вставлять переменные и специальные символы, а одинарные нет - вот и вся концептуальная разница.</p>
  <tt-tags>
    <tt-tag name="php">#php</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/g-hbK5rkQ</guid><link>https://blog.grom-it.ru/g-hbK5rkQ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/g-hbK5rkQ?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Именованные аргументы функций в PHP 8</title><pubDate>Sat, 03 Apr 2021 08:22:46 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/07/df/07df6105-1058-412a-9b71-ae874c2d8ef2.png"></media:content><category>php</category><tt:hashtag>php</tt:hashtag><tt:hashtag>php8</tt:hashtag><tt:hashtag>spatie</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/34/35/3435249b-1111-4ec4-ac89-a0a4ec3394f2.png"></img>В PHP 8 появилась возможность передавать аргументы в функции по именам.]]></description><content:encoded><![CDATA[
  <p>В PHP 8 появилась возможность передавать аргументы в функции по именам.</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/34/35/3435249b-1111-4ec4-ac89-a0a4ec3394f2.png" width="1076" />
  </figure>
  <p><strong>Какие преимущества?</strong></p>
  <p>Это позволяет передавать аргументы в функцию по имени в произвольном порядке, что будет особенно полезно, если функция принимает несколько не обязательных аргументов.</p>
  <p>Для тех, кто не в теме - ребята из <a href="http://spatie.be" target="_blank">spatie.be</a> сделали отличный видеокурс по новинкам в PHP 8.</p>
  <p>Ссылка на видеокурс - <a href="https://spatie.be/videos/front-line-php" target="_blank">https://spatie.be/videos/front-line-php</a></p>
  <tt-tags>
    <tt-tag name="php">#php</tt-tag>
    <tt-tag name="php8">#php8</tt-tag>
    <tt-tag name="spatie">#spatie</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/faqt6Mu6q</guid><link>https://blog.grom-it.ru/faqt6Mu6q?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/faqt6Mu6q?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>Разработка простейшего лендинга на OctoberCMS. Часть 2.</title><pubDate>Fri, 02 Apr 2021 15:42:56 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/26/f4/26f4a37b-1b2e-4758-81a8-fcfdf13e5b05.jpeg"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>landing</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/90/d5/90d51ae9-c9df-47f6-80c9-d0d03019f283.jpeg"></img>На чем мы остановились в предыдущей части? Мы создали следующее:]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/90/d5/90d51ae9-c9df-47f6-80c9-d0d03019f283.jpeg" width="1200" />
  </figure>
  <p>На чем мы остановились в предыдущей части? Мы создали следующее:</p>
  <ul>
    <li>плагин landing и миграцию</li>
    <li>проработали модель Page</li>
    <li>настроили поля формы и колонки для списка</li>
  </ul>
  <p>В итоге на финальном этапе первого урока у нас была следующая картина:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/43/14/4314c6b7-938c-4296-9fba-5685e3993f38.png" width="1026" />
  </figure>
  <p>Мы сделали один вариант страницы лендинга и заполнили его тестовым содержимым.</p>
  <h2>Компонент</h2>
  <blockquote>Компоненты, в рамкам OctoberCms это настраиваемые элементы, которые могут быть прикреплены к любой странице или шаблону. Мы можем создавать любое количество компонентов для любых задач, которые могут быть использованы на фронте.</blockquote>
  <p>Тут снова без выкрутасов - всё очень подробно расписано в документации OctoberCms - <a href="https://octobercms.com/docs/plugin/components" target="_blank">https://octobercms.com/docs/plugin/components</a></p>
  <p>Для нашей задачи нам предстоит создать всего один компонент. Назовем его <strong>Page.</strong></p>
  <p>Для создания компонента воспользуемся следующей командой:</p>
  <pre>php artisan create:component sandbox.landing page</pre>
  <p>В результате выполнения команды в директории нашего плагина будет создан компонент Page с подобной структурой</p>
  <figure class="m_original">
    <img src="https://teletype.in/files/7e/25/7e25e472-694f-4711-a29d-8284b1c60cce.png" width="239" />
  </figure>
  <p>Поправим файл Page.php под наши нужды</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253C%253Fphp%2520namespace%2520Sandbox%255CLanding%255CComponents%253B%250A%250Ause%2520Cms%255CClasses%255CComponentBase%253B%250A%250Aclass%2520Page%2520extends%2520ComponentBase%250A%257B%250A%2520%2520%2520%2520public%2520function%2520componentDetails%28%29%253A%2520array%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27name%27%2520%253D%253E%2520%27Landing%2520page%2520Component%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27description%27%2520%253D%253E%2520%27Simple%2520landing%2520page%2520component%27%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520public%2520function%2520defineProperties%28%29%253A%2520array%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27page%27%2520%253D%253E%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27title%27%2520%253D%253E%2520%27Page%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27description%27%2520%253D%253E%2520%27Select%2520the%2520page%2520to%2520be%2520displayed%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27type%27%2520%253D%253E%2520%27dropdown%27%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%255D%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%253B%250A%2520%2520%2520%2520%257D%250A%250A%250A%2520%2520%2520%2520%252F**%250A%2520%2520%2520%2520%2520*%2520%25D0%259F%25D0%25BE%25D0%25BB%25D1%2583%25D1%2587%25D0%25B8%25D0%25BC%2520%25D1%2581%25D0%25BF%25D0%25B8%25D1%2581%25D0%25BE%25D0%25BA%2520%25D1%2581%25D1%2582%25D1%2580%25D0%25B0%25D0%25BD%25D0%25B8%25D1%2586%2520%25D0%25B4%25D0%25BB%25D1%258F%2520%25D0%25B2%25D1%258B%25D0%25B1%25D0%25BE%25D1%2580%25D0%25B0%250A%2520%2520%2520%2520%2520*%2520%25D0%25B2%2520%25D0%25B2%25D1%258B%25D0%25BF%25D0%25B0%25D0%25B4%25D0%25B0%25D1%258E%25D1%2589%25D0%25B5%25D0%25BC%2520%25D1%2581%25D0%25BF%25D0%25B8%25D1%2581%25D0%25BA%25D0%25B5%2520%25D0%25B2%2520%25D0%25BD%25D0%25B0%25D1%2581%25D1%2582%25D1%2580%25D0%25BE%25D0%25B9%25D0%25BA%25D0%25B0%25D1%2585%2520%25D0%25BA%25D0%25BE%25D0%25BC%25D0%25BF%25D0%25BE%25D0%25BD%25D0%25B5%25D0%25BD%25D1%2582%25D0%25B0%2520%25D0%25BD%25D0%25B0%2520%25D1%2581%25D1%2582%25D1%2580%25D0%25B0%25D0%25BD%25D0%25B8%25D1%2586%25D0%25B5%250A%2520%2520%2520%2520%2520*%2520%2540return%2520array%250A%2520%2520%2520%2520%2520*%252F%250A%2520%2520%2520%2520public%2520function%2520getPageOptions%28%29%253A%2520array%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%255CSandbox%255CLanding%255CModels%255CPage%253A%253Alists%28%27title%27%252C%2520%27id%27%29%253B%250A%2520%2520%2520%2520%257D%250A%250A%2520%2520%2520%2520%252F**%250A%2520%2520%2520%2520%2520*%2520%25D0%259F%25D0%25BE%25D0%25BB%25D1%2583%25D1%2587%25D0%25B8%25D0%25BC%2520%25D1%2581%25D0%25BE%25D0%25B4%25D0%25B5%25D1%2580%25D0%25B6%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25B5%2520%25D0%25B2%25D1%258B%25D0%25B1%25D1%2580%25D0%25B0%25D0%25BD%25D0%25BD%25D0%25BE%25D0%25B9%2520%25D1%2581%25D1%2582%25D1%2580%25D0%25B0%25D0%25BD%25D0%25B8%25D1%2586%25D1%258B%250A%2520%2520%2520%2520%2520*%2520%25D0%25B8%2520%25D0%25B4%25D0%25BE%25D0%25B1%25D0%25B0%25D0%25B2%25D0%25B8%25D0%25BC%2520%25D0%25B5%25D0%25B5%2520%25D0%25B2%2520%25D0%25BF%25D0%25B5%25D1%2580%25D0%25B5%25D0%25BC%25D0%25B5%25D0%25BD%25D0%25BD%25D1%2583%25D1%258E%2520landing%250A%2520%2520%2520%2520%2520*%2520%2540return%2520void%250A%2520%2520%2520%2520%2520*%252F%250A%2520%2520%2520%2520public%2520function%2520onRun%28%29%253A%2520void%250A%2520%2520%2520%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2524this-%253Epage%255B%27landing%27%255D%2520%253D%2520%255CSandbox%255CLanding%255CModels%255CPage%253A%253Afind%28%2524this-%253Eproperty%28%27page%27%29%29%253B%250A%2520%2520%2520%2520%257D%250A%257D"></iframe>
  </figure>
  <p>И самое главное - не забыть зарегистрировать компонент в нашем плагине, для этого откроем Plugin.php и добавим следующее:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%252F**%250A%2520*%2520Registers%2520any%2520front-end%2520components%2520implemented%2520in%2520this%2520plugin.%250A%2520*%250A%2520*%2520%2540return%2520array%250A%2520*%252F%250Apublic%2520function%2520registerComponents%28%29%250A%257B%250A%2520%2520%2520%2520return%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%27Sandbox%255CLanding%255CComponents%255CPage%27%2520%253D%253E%2520%27Page%27%250A%2520%2520%2520%2520%255D%253B%250A%257D"></iframe>
  </figure>
  <p>Итак, мы создали простейший компонент, который берет из настроек идентификатор страницы и отдает ее в переменную landing.</p>
  <p>На самом деле, даже этот вариант можно улучшать и улучшать, но цель уроков - ознакомление с возможностями.</p>
  <h2><strong>Тема для лендинга.</strong></h2>
  <p>Для нашей лендинговой темы, мы не будем изобретать велосипед и возьмем, уже полюбившуюся всем, стартовую тему demo - просто скопируем ее в папку landing.</p>
  <figure class="m_original">
    <img src="https://teletype.in/files/10/38/1038d8c0-aed3-40c9-b9ec-da656e425bf3.png" width="189" />
  </figure>
  <p>Поправим файл <code>themes/landing/theme.yaml</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=name%253A%2520Landing%2520theme%250Adescription%253A%2520%27Landing%2520demo%2520theme%27%250Aauthor%253A%2520OctoberCMS%250Ahomepage%253A%2520%27http%253A%252F%252Foctobercms.com%27%250Acode%253A%2520%27landing-theme%27"></iframe>
  </figure>
  <p>Далее подправим файл шаблона <code>themes/landing/pages/home.htm</code>, подготовив его для дальнейшей работы.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=title%2520%253D%2520%2522Demonstration%2522%250Aurl%2520%253D%2520%2522%252F%2522%250Alayout%2520%253D%2520%2522default%2522%250A%253D%253D%250A%250A%253Cdiv%2520class%253D%2522container%2522%253E%250A%2509Our%2520landing%2520content%250A%253C%252Fdiv%253E"></iframe>
  </figure>
  <p>Готово. Теперь наш сайт выглядит примерно так:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/ae/32/ae32d5c1-711c-49cc-9f5d-dabfede14d51.png" width="1169" />
  </figure>
  <p>Не очень круто =)</p>
  <h2>Подключение компонента и еще немного правок шаблона.</h2>
  <p>В панели управления переходим в раздел CMS, открываем шаблон нашей темы и бросаем туда компонент Landing.</p>
  <p>В настройках компонента выбираем страницу, содержимое которой хотим отображать.</p>
  <blockquote>Страницу South Park: The Fractured but Whole мы создали и наполнили контентом в <a href="https://blog.grom-it.ru/N9epRWxGX" target="_blank">первой части</a> этого повествования.</blockquote>
  <figure class="m_column">
    <img src="https://teletype.in/files/9f/cd/9fcd12c0-09d2-443c-b861-93333e164363.png" width="1171" />
  </figure>
  <p>Компонент подключен к шаблону, соответственно свойства и методы его теперь доступны на всех страницах, которые используют этот шаблон.</p>
  <p>Далее отредактируем файл <code>themes/landing/partials/site/header.htm</code></p>
  <p>Удалим всё - добавим такое содержимое:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253D%253D%250Afunction%2520onStart%28%29%250A%257B%250A%2520%2520%2520%2520%2524this%255B%27backendUrl%27%255D%2520%253D%2520Backend%253A%253Aurl%28%27%252F%27%29%253B%250A%257D%250A%253D%253D%250A%253C%21--%2520Nav%2520--%253E%250A%253Cnav%2520id%253D%2522layout-nav%2522%2520class%253D%2522navbar%2520navbar-inverse%2520navbar-fixed-top%2520navbar-autohide%2522%2520role%253D%2522navigation%2522%253E%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522container%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522navbar-header%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Ca%2520class%253D%2522navbar-brand%2522%2520href%253D%2522%257B%257B%2520%27home%27%257Cpage%2520%257D%257D%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257B%257B%2520landing.title%2520%257D%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fa%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fdiv%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%253C%252Fnav%253E"></iframe>
  </figure>
  <p>{{ landing.title }} - это название нашей страницы</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/8b/91/8b915462-d401-4af6-ae83-dea3d9b2e9a3.png" width="808" />
  </figure>
  <p>После чего, наш сайт стал выглядеть так:</p>
  <p> </p>
  <figure class="m_column">
    <img src="https://teletype.in/files/3e/46/3e46b62d-3bca-4482-9de5-0d5075b0d784.png" width="1195" />
  </figure>
  <p>Уже лучше!</p>
  <h2>Группы контента</h2>
  <p>Приступим к самому интересному - будем оживлять динамические блоки с содержимым, которые мы уже давным давно создали и даже заполнили контентом.</p>
  <p>Группы мы определили при создании модели и формы - это:</p>
  <ul>
    <li>text</li>
    <li>image</li>
    <li>features</li>
    <li>slider</li>
  </ul>
  <p>Чтобы убедиться - вернемся к файлу <code>plugins/sandbox/landing/models/page/groups.yaml</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=text%253A%250A%2520%2520%2520%2520name%253A%2520Text%2520block%250A%2520%2520%2520%2520description%253A%2520Text%2520block%2520with%2520richeditor%250A%2520%2520%2520%2520icon%253A%2520icon-file-text-o%250A%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520text%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Text%2520block%2520content%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520richeditor%250A%250Aimage%253A%250A%2520%2520%2520%2520name%253A%2520Single%2520image%250A%2520%2520%2520%2520description%253A%2520Block%2520with%2520single%2520image%250A%2520%2520%2520%2520icon%253A%2520icon-image%250A%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520image%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Image%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520mediafinder%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520mode%253A%2520image%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520imageHeight%253A%2520150%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520imageWidth%253A%2520150%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520thumbOptions%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520mode%253A%2520crop%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520extension%253A%2520auto%250A%250Afeatures%253A%250A%2520%2520%2520%2520name%253A%2520Features%250A%2520%2520%2520%2520description%253A%2520Features%2520simple%2520block%250A%2520%2520%2520%2520icon%253A%2520icon-star%250A%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520title%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Feature%2520title%250A%2520%2520%2520%2520%2520%2520%2520%2520features_items%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520repeater%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520form%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520feature_title%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Feature%2520title%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520span%253A%2520left%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520feature_description%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Feature%2520description%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520span%253A%2520right%250A%250Aslider%253A%250A%2520%2520%2520%2520name%253A%2520Slider%250A%2520%2520%2520%2520description%253A%2520Block%2520with%2520slider%2520images%250A%2520%2520%2520%2520icon%253A%2520icon-play-circle-o%250A%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520images%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Slides%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520repeater%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520form%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520fields%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520image%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520label%253A%2520Slide%2520Image%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520type%253A%2520mediafinder%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520mode%253A%2520image%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520imageHeight%253A%2520150%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520imageWidth%253A%2520150%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520thumbOptions%253A%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520mode%253A%2520crop%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520extension%253A%2520auto"></iframe>
  </figure>
  <p>В нашей теме в разделе в <code>themes/landing/partials</code> создадим папку <code>content</code>, где сложим шаблоны для каждой группы контента.</p>
  <p>В папке <code>themes/landing/partials/content</code>  создадим следующие файлы:</p>
  <ul>
    <li>text.htm</li>
    <li>image.htm</li>
    <li>features.htm</li>
    <li>slider.htm</li>
  </ul>
  <p>Должна получиться такая структура:</p>
  <figure class="m_original">
    <img src="https://teletype.in/files/54/35/54350bba-4d47-4d91-b758-7ec4ca0d6580.png" width="249" />
  </figure>
  <p>Далее вернемся в файлу <code>themes/landing/pages/home.htm</code> и отредактируем его следующим образом.</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=title%2520%253D%2520%2522Demonstration%2522%250Aurl%2520%253D%2520%2522%252F%2522%250Alayout%2520%253D%2520%2522default%2522%250A%253D%253D%250A%250A%253Cdiv%2520class%253D%2522container%2522%253E%250A%2520%2520%2520%2520%257B%2525%2520for%2520blockKey%252C%2520blockContent%2520in%2520landing.content%2520%2525%257D%250A%2520%2520%2520%2520%257B%2525%2520partial%2520%27content%252F%27%7E%2520blockContent._group%2520blockContent%2520%253D%2520blockContent%2520blockKey%2520%253D%2520blockKey%2520%2525%257D%250A%2520%2520%2520%2520%257B%2525%2520endfor%2520%2525%257D%250A%253C%252Fdiv%253E"></iframe>
  </figure>
  <p>Что означает эта конструкция:</p>
  <p>В цикле мы перебираем содержимое landing.content, это содержимое состоит из именованных групп и содержимого этих групп.</p>
  <p>Иными словами зная, что это за группа (из свойства _group), мы можем динамически отправить ключ и содержимое группы в соответствующую партиалку.</p>
  <h2>Партиалим!</h2>
  <h3>text.htm</h3>
  <p>Начнем с файла <code>themes/landing/partials/content/text.htm</code></p>
  <p>Приведем его к следующему виду:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253Cdiv%2520id%253D%2522text-%257B%257B%2520blockKey%2520%257D%257D%2522%2520class%253D%2522content-block%2520text-block%2522%253E%250A%2520%2520%2520%2520%257B%257B%2520blockContent.text%257Craw%2520%257D%257D%250A%253C%252Fdiv%253E"></iframe>
  </figure>
  <h3>image.htm</h3>
  <p>Партиалку для отображения картинки <code>themes/landing/partials/content/image.htm</code>  приведем к следующему виду:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253Cdiv%2520id%253D%2522image-%257B%257B%2520blockKey%2520%257D%257D%2522%2520class%253D%2522content-block%2520image-block%2522%253E%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522image-block__image%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cimg%2520src%253D%2522%257B%257B%2520blockContent.image%257Cmedia%2520%257D%257D%2522%253E%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%253C%252Fdiv%253E"></iframe>
  </figure>
  <h3>features.htm</h3>
  <p>Партиалку для отображения преимуществ <code>themes/landing/partials/content/features.htm</code> тоже модифицируем:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253Cdiv%2520id%253D%2522features-%257B%257B%2520blockKey%2520%257D%257D%2522%2520class%253D%2522content-block%2520features-block%2522%253E%250A%2520%2520%2520%2520%253Ch2%253E%257B%257B%2520blockContent.title%2520%257D%257D%253C%252Fh2%253E%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522features-block__features%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%257B%2525%2520for%2520feature%2520in%2520blockContent.features_items%2520%2525%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522features-block__feature%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522features-block__feature-title%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257B%257B%2520feature.feature_title%2520%257D%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522features-block__feature-description%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257B%257B%2520feature.feature_description%2520%257D%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%257B%2525%2520endfor%2520%2525%257D%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%253C%252Fdiv%253E"></iframe>
  </figure>
  <h3>slider.htm</h3>
  <p>И наконец слайдер <code>themes/landing/partials/content/slider.htm</code> мы поправим в два этапа.</p>
  <p>Для примера слайдера используем библиотеку <a href="https://github.com/kenwheeler/slick" target="_blank">https://github.com/kenwheeler/slick</a></p>
  <p>Как и написано в документации, подключим стили в файл шаблона <code>themes/landing/layouts/default.htm</code> в раздел <code>head</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253Chead%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520charset%253D%2522utf-8%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Ctitle%253EOctober%2520CMS%2520-%2520%257B%257B%2520this.page.title%2520%257D%257D%253C%252Ftitle%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520name%253D%2522description%2522%2520content%253D%2522%257B%257B%2520this.page.meta_description%2520%257D%257D%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520name%253D%2522title%2522%2520content%253D%2522%257B%257B%2520this.page.meta_title%2520%257D%257D%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520name%253D%2522author%2522%2520content%253D%2522OctoberCMS%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520name%253D%2522viewport%2522%2520content%253D%2522width%253Ddevice-width%252C%2520initial-scale%253D1.0%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cmeta%2520name%253D%2522generator%2522%2520content%253D%2522OctoberCMS%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Clink%2520rel%253D%2522icon%2522%2520type%253D%2522image%252Fpng%2522%2520href%253D%2522%257B%257B%2520%27assets%252Fimages%252Foctober.png%27%257Ctheme%2520%257D%257D%2522%253E%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%21--%2520slick%2520--%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Clink%2520rel%253D%2522stylesheet%2522%2520type%253D%2522text%252Fcss%2522%2520href%253D%2522%252F%252Fcdn.jsdelivr.net%252Fnpm%252Fslick-carousel%25401.8.1%252Fslick%252Fslick.css%2522%252F%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Clink%2520rel%253D%2522stylesheet%2522%2520type%253D%2522text%252Fcss%2522%2520href%253D%2522%252F%252Fcdn.jsdelivr.net%252Fnpm%252Fslick-carousel%25401.8.1%252Fslick%252Fslick-theme.css%2522%252F%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253C%21--%2520slick%2520--%253E%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Clink%2520href%253D%2522%257B%257B%2520%27assets%252Fcss%252Fvendor.css%27%257Ctheme%2520%257D%257D%2522%2520rel%253D%2522stylesheet%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Clink%2520href%253D%2522%257B%257B%2520%27assets%252Fcss%252Ftheme.css%27%257Ctheme%2520%257D%257D%2522%2520rel%253D%2522stylesheet%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%257B%2525%2520styles%2520%2525%257D%250A%253C%252Fhead%253E"></iframe>
  </figure>
  <p>и в конец шаблона добавим js-библиотеку:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%2520%253C%21--%2520Scripts%2520--%253E%250A%2520%2520%2520%2520%253Cscript%2520src%253D%2522%257B%257B%2520%27assets%252Fvendor%252Fjquery.js%27%257Ctheme%2520%257D%257D%2522%253E%253C%252Fscript%253E%250A%2520%2520%2520%2520%253Cscript%2520src%253D%2522%257B%257B%2520%27assets%252Fvendor%252Fbootstrap.js%27%257Ctheme%2520%257D%257D%2522%253E%253C%252Fscript%253E%250A%2520%2520%2520%2520%253C%21--%2520slick%2520--%253E%250A%2520%2520%2520%2520%253Cscript%2520type%253D%2522text%252Fjavascript%2522%2520src%253D%2522%252F%252Fcdn.jsdelivr.net%252Fnpm%252Fslick-carousel%25401.8.1%252Fslick%252Fslick.min.js%2522%253E%253C%252Fscript%253E%250A%2520%2520%2520%2520%253C%21--%2520slick%2520--%253E%250A%2520%2520%2520%2520%253Cscript%2520src%253D%2522%257B%257B%2520%27assets%252Fjavascript%252Fapp.js%27%257Ctheme%2520%257D%257D%2522%253E%253C%252Fscript%253E%250A%2520%2520%2520%2520%257B%2525%2520framework%2520extras%2520%2525%257D%250A%2520%2520%2520%2520%257B%2525%2520scripts%2520%2525%257D"></iframe>
  </figure>
  <p>Далее отредактируем файл <code>themes/landing/partials/content/slider.htm</code> и приведем его к следующему виду:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=htmlmixed&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253Cdiv%2520id%253D%2522slider-%257B%257B%2520blockKey%2520%257D%257D%2522%2520class%253D%2522content-block%2520slider-block%2522%253E%250A%2520%2520%2520%2520%253Ch2%253E%257B%257BblockContent.hint%257D%257D%253C%252Fh2%253E%250A%2520%2520%2520%2520%253Cdiv%2520class%253D%2522slider-block__slider%2522%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%257B%2525%2520for%2520image%2520in%2520blockContent.images%2520%2525%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520%253Cdiv%2520class%253D%2522slider-block__slide%2522%2520style%253D%2522background-image%253A%2520url%28%27%257B%257B%2520image.image%257Cmedia%2520%257D%257D%27%29%253B%2522%253E%253C%252Fdiv%253E%250A%2520%2520%2520%2520%2520%2520%2520%2520%257B%2525%2520endfor%2520%2525%257D%250A%2520%2520%2520%2520%253C%252Fdiv%253E%250A%253C%252Fdiv%253E%250A%250A%257B%2525%2520put%2520scripts%2520%2525%257D%250A%253Cscript%253E%250A%2520%2520%2520%2520%2524%28%27%2523slider-%257B%257B%2520blockKey%2520%257D%257D%2520.slider-block__slider%27%29.slick%28%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520dots%253A%2520true%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520infinite%253A%2520true%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520speed%253A%2520300%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520slidesToShow%253A%25201%252C%250A%2520%2520%2520%2520%257D%29%253B%250A%253C%252Fscript%253E%250A%257B%2525%2520endput%2520%2525%257D"></iframe>
  </figure>
  <h3>Немного CSS</h3>
  <p>Мы не зря добавили css-классы в наши partial-файлы. Приправим наш урок небольшой порцией css.</p>
  <p>В конец файла <code>themes/landing/assets/css/theme.css</code> добавим немного своего:</p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=css&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=.content-block%2520%257B%250A%2520%2520%2520%2520margin-bottom%253A%252040px%253B%250A%257D%250A%250A.features-block%2520h2%2520%257B%250A%2520%2520%2520%2520text-align%253A%2520center%253B%250A%257D%250A%250A.features-block__features%2520%257B%250A%2520%2520%2520%2520display%253A%2520flex%253B%250A%2520%2520%2520%2520justify-content%253A%2520center%253B%250A%257D%250A%250A.features-block__feature%2520%257B%250A%2520%2520%2520%2520padding%253A%252030px%253B%250A%2520%2520%2520%2520flex%253A%25201%25201%25200%253B%250A%257D%250A%250A.features-block__feature-title%2520%257B%250A%2520%2520%2520%2520font-size%253A%252018px%253B%250A%2520%2520%2520%2520text-align%253A%2520center%253B%250A%257D%250A%250A.features-block__feature-description%2520%257B%250A%2520%2520%2520%2520font-size%253A%252048px%253B%250A%2520%2520%2520%2520text-align%253A%2520center%253B%250A%257D%250A%250A.image-block%2520%257B%250A%2520%2520%2520%2520display%253A%2520flex%253B%250A%2520%2520%2520%2520justify-content%253A%2520center%253B%250A%257D%250A%250A.image-block__image%2520%257B%250A%2520%2520%2520%2520width%253A%252070%2525%253B%250A%257D%250A%250A.image-block__image%2520img%2520%257B%250A%2520%2520%2520%2520width%253A%2520100%2525%253B%250A%257D%250A%250A.slider-block__slide%2520%257B%250A%2520%2520%2520%2520height%253A%2520500px%253B%250A%2520%2520%2520%2520background-position%253A%2520center%253B%250A%2520%2520%2520%2520background-repeat%253A%2520no-repeat%253B%250A%2520%2520%2520%2520background-size%253A%2520cover%253B%250A%257D"></iframe>
  </figure>
  <h2>Что в итоге?</h2>
  <p>В итоге у нас должно получиться следующее:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/da/d1/dad127b7-7789-43b1-bb73-1de3b94497de.png" width="1920" />
  </figure>
  <p>Выглядит вполне приемлемо!</p>
  <h2>Немного универсализации</h2>
  <p>Что же мы хотим - мы хотим в дальнейшем посадить на один движок OctoberCMS несколько лендингов, чем больше - тем лучше!</p>
  <p>Можно заморочиться и написать свой плагин для мультисайта, но в рамках урока используем готовые наработки из маркетплейса OctoberCms - плагин <a href="https://octobercms.com/plugin/briddle-multisite" target="_blank">Multiple Websites Manager Lite</a> - простой, понятный и бесплатный.</p>
  <h3>Что нужно сделать:</h3>
  <ul>
    <li>установить плагин <code>Briddle.Multisite</code></li>
    <li>суметь настроить домен третьего уровня (об этом не в этом уроке)</li>
    <li>отредактировать конфиг плагина <code>/plugins/briddle/multisite/config</code></li>
  </ul>
  <p>Для примера мы скопируем нашу тему <code>landing</code> и назовем ее <code>landing2</code></p>
  <p>Не забываем отредактировать файл <code>themes/landing2/theme.yaml</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=yaml&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=name%253A%2520Landing%2520theme%25202%250Adescription%253A%2520%27Landing%2520demo%2520theme%25202%27%250Aauthor%253A%2520OctoberCMS%250Ahomepage%253A%2520%27http%253A%252F%252Foctobercms.com%27%250Acode%253A%2520%27landing-theme-2%27"></iframe>
  </figure>
  <p>Далее отредактируем <code>/plugins/briddle/multisite/config</code></p>
  <figure class="m_column">
    <iframe src="https://carbon.now.sh/embed?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=monokai&wt=none&l=text%2Fx-php&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=false&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=%253C%253Fphp%250A%250Areturn%2520%255B%250A%2520%2520%2520%2520%27sites%27%2520%253D%253E%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27host%27%2520%253D%253E%2520%27sandbox.test%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27theme%27%2520%253D%253E%2520%27landing%27%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%255B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27host%27%2520%253D%253E%2520%27landing2.sandbox.test%27%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%27theme%27%2520%253D%253E%2520%27landing2%27%250A%2520%2520%2520%2520%2520%2520%2520%2520%255D%250A%2520%2520%2520%2520%255D%250A%255D%253B"></iframe>
  </figure>
  <p>В админке сайта добавляем новый материал:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/41/69/41693313-8220-412a-b6df-b748d7f22ae6.png" width="730" />
  </figure>
  <p>Теперь заходим в админку сайта, но уже по адресу <code>landing2.sandbox.test</code></p>
  <p>Переходим в раздел CMS и отредактируем файл шаблона <code>layouts/default.htm</code></p>
  <figure class="m_column">
    <img src="https://teletype.in/files/0d/00/0d00be02-f92f-40ba-a3f9-1668fc0bf50a.png" width="1030" />
  </figure>
  <p>Выбираем новую созданную страницу.</p>
  <h2>Результат</h2>
  <p>По итогу этого небольшого гайда у нас должно быть в наличии:</p>
  <ul>
    <li>плагин для создания лендинговых страниц</li>
    <li>две темы (сайта) , которые работают с одним движком</li>
    <li>и такой результат:</li>
  </ul>
  <figure class="m_column">
    <img src="https://teletype.in/files/fd/f3/fdf31ec2-9cee-4d83-8829-23702ad00367.jpeg" width="1600" />
  </figure>
  <h2>Ссылки</h2>
  <p><a href="https://github.com/gromitsoft/sandbox" target="_blank">https://github.com/gromitsoft/sandbox</a> - репозиторий с исходным кодом<br /><a href="https://blog.grom-it.ru/N9epRWxGX" target="_blank">https://blog.grom-it.ru/N9epRWxGX</a> - первая часть урока</p>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="landing">#landing</tt-tag>
  </tt-tags>

]]></content:encoded></item><item><guid isPermaLink="true">https://blog.grom-it.ru/T7Jo76xx1</guid><link>https://blog.grom-it.ru/T7Jo76xx1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit</link><comments>https://blog.grom-it.ru/T7Jo76xx1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=gromit#comments</comments><dc:creator>gromit</dc:creator><title>QuickActions в OctoberCMS</title><pubDate>Tue, 30 Mar 2021 17:33:12 GMT</pubDate><media:content medium="image" url="https://teletype.in/files/c1/ec/c1ecc05e-fd45-48f6-952e-8f632f5b7ffc.jpeg"></media:content><category>OctoberCMS</category><tt:hashtag>octobercms</tt:hashtag><tt:hashtag>tips</tt:hashtag><tt:hashtag>backend</tt:hashtag><description><![CDATA[<img src="https://teletype.in/files/6d/29/6d29d0cf-bcf0-4d96-b8a0-14c671da1020.jpeg"></img>Заметка из разряда - &quot;Всякие интересности в OctoberCMS&quot;]]></description><content:encoded><![CDATA[
  <figure class="m_column">
    <img src="https://teletype.in/files/6d/29/6d29d0cf-bcf0-4d96-b8a0-14c671da1020.jpeg" width="1200" />
  </figure>
  <p>Заметка из разряда - &quot;Всякие интересности в OctoberCMS&quot;</p>
  <p>В ноябре 2020 г. был замечен интересный <a href="https://github.com/octobercms/october/pull/5344" target="_blank">pull-request</a> в репозиторий OctoberCMS.</p>
  <blockquote>This PR adds support for defining quick actions in the main navigation in the Backend. Quick actions in this case are defined as the single-purpose icon links located to the left of the user menu - the only one currently available in an October CMS install is the link to preview the website.</blockquote>
  <p>Другими словами этот PR добавляет &quot;быстрые действия&quot; в основной навигации в панели управления, расположенные слева от ссылки для предварительного просмотра сайта.</p>
  <h2>Небольшой How-To</h2>
  <p>Для того, чтобы новая фича заработала - нам нужна новая установка OctoberCMS из композера:</p>
  <pre>composer create-project october/october myoctober</pre>
  <p>и обновиться до последней версии через тот же composer:</p>
  <pre>composer update</pre>
  <h3>Создадим тестовый плагин для эксперимента</h3>
  <p>Для создания тестового плагина QuickActions выполним в консоли:</p>
  <pre>php artisan create:plugin sandbox.quickactions</pre>
  <p>Далее, отредактируем файл Plugin.php, добавим в него следующее:</p>
  <pre>public function registerQuickActions()
{
    return [
        &#x27;help&#x27; =&gt; [
            &#x27;label&#x27; =&gt; &#x27;Read the documentation &#x27;,
            &#x27;icon&#x27; =&gt; &#x27;icon-question-circle&#x27;,
            &#x27;url&#x27; =&gt; Backend::url(&#x27;read-the-docs&#x27;),
        ],        
    ];
}</pre>
  <p>Voila, теперь в панели управления мы увидим такое новшество:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/38/8d/388d911c-c2d5-4d33-a197-6f8362eb9736.png" width="2002" />
  </figure>
  <p>Пойдем дальше, добавим кастомную svg-иконку. Очень часто в проектах мы используем ресурс <a href="https://www.svgrepo.com/" target="_blank">https://www.svgrepo.com/</a> как источник иконок.</p>
  <p>Добавим быструю ссылку для связи с разработчиками сайта. Для иконки возьмем первую попавшуюся из этого набора <a href="https://www.svgrepo.com/vectors/mail/multicolor/" target="_blank">https://www.svgrepo.com/vectors/mail/multicolor/</a> и добавим ее в наш плагин в папку:</p>
  <pre>plugins/sandbox/quickactions/assets/svg</pre>
  <p>Далее отредактируем метод <code>registerQuickActions()</code> в файле <code>Plugin.php</code></p>
  <pre>public function registerQuickActions()
{
    return [
        &#x27;help&#x27; =&gt; [
            &#x27;label&#x27; =&gt; &#x27;Read the documentation&#x27;,
            &#x27;icon&#x27; =&gt; &#x27;icon-question-circle&#x27;,
            &#x27;url&#x27; =&gt; Backend::url(&#x27;read-the-docs&#x27;),
        ],
        &#x27;mail&#x27; =&gt; [
            &#x27;label&#x27; =&gt; &#x27;Send email to developers&#x27;,
            &#x27;iconSvg&#x27; =&gt; &#x27;/plugins/sandbox/quickactions/assets/svg/mail.svg&#x27;,
            &#x27;url&#x27; =&gt; Backend::url(&#x27;mail-to-devs&#x27;),
        ],           
    ];
}</pre>
  <p>Смотрим что получилось:</p>
  <figure class="m_column">
    <img src="https://teletype.in/files/da/0a/da0a1e5c-483a-4eb8-a078-5967d6dd07dc.png" width="1992" />
  </figure>
  <p>В своих проектах мы пока не применяли данную возможность, но надеемся, что кому-то это пригодится!</p>
  <tt-tags>
    <tt-tag name="octobercms">#octobercms</tt-tag>
    <tt-tag name="tips">#tips</tt-tag>
    <tt-tag name="backend">#backend</tt-tag>
  </tt-tags>

]]></content:encoded></item></channel></rss>