Статьи

3 способа реализации встраиваемых пользовательских значков

Одним из отличных способов органичного продвижения вашего приложения является предоставление «значков»; фрагменты контента, которые люди могут вставлять на свои сайты.

значок

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

Some examples of embedded content

В этой статье я расскажу о некоторых способах реализации этого.

Настройка нашего примера приложения

Весь код из этого урока доступен на Github . Там также онлайн демо .

Сначала мы определим зависимости нашего приложения с помощью Composer:

"silex/silex": "~2.0@dev", "twig/twig": ">=1.8,<2.0-dev", "smottt/wideimage": "dev-master" 

Теперь, в index.php , давайте добавим сгенерированный Composer автозагрузчик, добавим наш оператор use , инициализируем наше приложение Silex и настроим шаблон Twig:

 require_once __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request, Symfony\Component\HttpFoundation\Response, WideImage\WideImage; $app = new Silex\Application(); // Register the Twig service provider $app->register(new Silex\Provider\TwigServiceProvider(), array( 'twig.path' => __DIR__.'/../views', )); 

Давайте создадим хранилище данных пользователей с некоторой информацией о них, которая послужит основой для нашего примера «значков». Для простоты мы будем использовать статический массив; на практике вы используете базу данных, но это должно быть довольно просто, чтобы заменить это на что-то более динамичное. Мы вставим его в контейнер приложения следующим образом:

 /** * For simplicity, our datastore is a really simple, static array */ $app['datastore'] = function(){ return [ 'users' => [ 'dave' => [ 'avatar' => 'man1.png', 'trophies' => 1, 'rank' => 'Novice', ], 'jim' => [ 'avatar' => 'man2.png', 'trophies' => 2, 'rank' => 'Intermediate', ], 'helen' => [ 'avatar' => 'woman1.png', 'trophies' => 4, 'rank' => 'Grand Master', ], ] ]; }; 

Теперь, когда у нас есть базовое приложение, настроенное вместе с некоторыми данными, давайте рассмотрим три подхода к тому, как вы можете предоставить встраиваемый «значок», который отображает эти данные для данного пользователя.

IFrames

IFrames — возможно, грязное слово в веб-кругах, возможно, заслуженно. Но они представляют собой общий и практичный подход для встраивания контента с одного сайта в другой.

Давайте начнем с этого подхода; позже мы можем использовать часть кода, когда попробуем метод JavaScript.

Начните с простого шаблона Twig, который создает некоторый HTML — со встроенными стилями для минимизации HTTP-запросов — который содержит наш «значок» в виде <div> :

 <!-- /views/badge.twig --> <html> <head> <title>{{ username }}</title> <style> .badge { width: 250px; height: 80px; border: solid 1px #ccc; clear: both; } .avatar { float: left; width: 80px; } .badge .avatar img { margin: 10px 0 0 10px } .badge .info { width: 170px; float: right; } .badge .info h3 { margin: 0.25em 0; } .badge .info h4 { margin: 0.25em 0; color: #666; } </style> </head> <body> <div class="badge"> <div class="avatar"> <img src="{{ imagepath }}/{{ user.avatar }}"> </div> <div class="info"> <h3>{{ username }}</h3> <h4>{{ user.rank }}</h4> <div class="trophies"> {% for i in 1..user.trophies %} <img src="{{ imagepath }}/trophy.png"> {% endfor %} </div> </div> </div> </body> </html> 

Это все довольно просто. Обратите внимание, как мы включаем переменную с именем imagepath которую мы установим на стороне сервера, которая позаботится об одном очень важном аспекте — любые включаемые нами изображения должны ссылаться с использованием абсолютных URL-адресов.

Теперь соответствующий маршрут:

 /** * Dynamically-generated HTML for embedding in an iFrame */ $app->get('/iframe/{username}', function(Request $request, $username) use ($app) { // Check that the user in question exists if (!isset($app['datastore']['users'][$username])) { // No user with that username, throw a 404 $app->abort(404, "User $username does not exist."); } // Get the user record $user = $app['datastore']['users'][$username]; return $app['twig']->render('badge.twig', [ 'username' => $username, 'imagepath' => ( ($request->server->get('HTTP_PORT') == 443) ? 'https' : 'http' ) . '://' . $request->server->get('HTTP_HOST') . '/images', 'user' => $user, ] ); }); 

Все довольно просто. Бит, который заполняет $imagepath немного быстрый и грязный, но он пока сделает свою работу.

Внедрить это в сторонний сайт очень просто:

 <iframe src="/iframe/dave" width="300" height="100"></iframe> 

Позже мы рассмотрим некоторые вещи, о которых вам нужно подумать, используя подход iframe; сейчас перейдем к методу номер два.

Динамически созданные изображения

Один из самых простых способов реализовать это — предоставить URL-адрес изображения, которое создается на стороне сервера.

Вот скриншот того изображения, которое мы собираемся создать:

The dynamically-generated image we're going to create

Ладно, он не получит никаких наград за дизайн, но вы можете использовать тот же подход для создания гораздо более привлекательных изображений, чем этот.

Вы найдете необходимые ресурсы — фоновые и трофейные изображения, аватары и шрифты — в репозитории примера приложения .

Вот пример кода для динамического создания встраиваемого изображения:

 /** * Dynamically-generated image */ $app->get('/image/{username}', function($username) use ($app) { // Check that the user in question exists if (!isset($app['datastore']['users'][$username])) { // No user with that username, throw a 404 $app->abort(404, "User $username does not exist."); } // Get the user record $user = $app['datastore']['users'][$username]; // Load the background $background = WideImage::load(__DIR__.'/../resources/images/background.png'); // Load the avatar $avatar = WideImage::load(__DIR__.'/images/' . $user['avatar']); // Load the trophy image $trophy = WideImage::load(__DIR__.'/images/trophy.png'); // Paste the avatar onto the background $im = $background->merge($avatar, 10, 20); // Get the canvas $canvas = $im->getCanvas(); // Set the font for the username $canvas->useFont(__DIR__.'/../resources/fonts/VeraBd.ttf', 12, $im->allocateColor(0, 0, 0)); // Write the username onto the canvas $canvas->writeText(70, 15, $username); // Choose a slightly smaller, non-bold font $canvas->useFont(__DIR__.'/../resources/fonts/Vera.ttf', 9, $im->allocateColor(0, 0, 0)); // Write the rank $canvas->writeText(70, 35, $user['rank']); // Now add the appropriate number of trophies $x = 70; for ($i = 0; $i < $user['trophies']; $i++) { $im = $im->merge($trophy, $x, 55); $x += 20; } // Finally, output the image to the screen return $im->output('png'); }); 

Это в значительной степени самодокументировано, и должно быть довольно просто адаптироваться к вашим потребностям или с лучшими изображениями. Обратите внимание, что мы берем фоновое изображение из не доступного через Интернет каталога ( resources ), но аватары и значок трофея находятся в public каталоге.

Встраивание этого в сторонний сайт не может быть проще:

 <img src="http://example.com/image/helen"> 

Вы заметите, что нет расширения файла; это не имеет значения, так как метод output() WideImage установит для вас соответствующие заголовки.

Есть несколько улучшений, которые мы могли бы сделать. Во-первых, мы генерируем новое изображение при каждом запросе. Однако вы можете использовать метод saveToFile() для кэширования результатов, например так:

 $im->saveToFile('/path/to/badge.png'); 

Также может быть лучше предоставить изображение по умолчанию, когда запрошенный пользователь не может быть найден, вместо выдачи ошибки 404.

Теперь о третьем и последнем подходе.

Javascript

Использование JavaScript для динамического создания встроенного содержимого является одним из наиболее распространенных и, возможно, наиболее гибким подходом.

Снова мы собираемся сгенерировать некоторый HTML, но на этот раз мы вернем простой фрагмент JavaScript, который запишет его на страницу хоста. Все, что требуется, — это чтобы веб-сайт хоста вставлял простой <script> где они хотят, чтобы наш контент появлялся

Мы будем повторно использовать шаблон Twig из ранее, но на этот раз маршрут выглядит немного иначе:

 /** * Dynamically-generated JavaScript */ $app->get('/js/{username}', function(Request $request, $username) use ($app) { // Check that the user in question exists if (!isset($app['datastore']['users'][$username])) { // No user with that username, throw a 404 $app->abort(404, "User $username does not exist."); } // Get the user record $user = $app['datastore']['users'][$username]; // Build the HTML $html = $app['twig']->render('badge.twig', [ 'username' => $username, 'imagepath' => ( ($request->server->get('HTTP_PORT') == 443) ? 'https' : 'http' ) . '://' . $request->server->get('HTTP_HOST') . '/images', 'user' => $user, ] ); // Minify the HTML, ensuring we wind up with one long string $minified = preg_replace( array( '/ {2,}/', '/<!--.*?-->|\t|(?:\r?\n[ \t]*)+/s' ), array( ' ', '' ), $html ); // Return a document.write with the minified, populated HTML as its argument return new Response( sprintf('document.write(\'%s\');', $minified), 200, [ 'Content-Type', 'text/javascript' ] ); }); 

Первая часть идентична подходу iframe. На этот раз, однако, мы генерируем простой document.write . Прежде чем мы сможем это сделать, мы используем небольшую магию preg_replace для preg_replace получающегося HTML-кода (который также гарантирует, что он будет все в одной строке), а затем вставляем его в какой-то очень простой динамически создаваемый JavaScript.

Внедрить это в страницу так же просто:

 <div><script src="/js/jim"></script></div> 

Строго говоря, нам даже не нужен этот контейнер DIV, но его можно использовать для применения стилей на хост-сайте.

Теперь, когда мы рассмотрели три подхода, давайте рассмотрим некоторые вещи, которые необходимо учитывать при принятии решения, какой из этих подходов выбрать.

Соображения

При выборе подхода к этой проблеме, есть несколько вещей, которые вам нужно взвесить.

CMS,

Если вы хотите, чтобы люди могли встраивать контент в основную часть контента, управляемого CMS, или в сообщения в блоге, следует иметь в виду, что любое ценное программное обеспечение CMS или блога будет блокировать определенные типы контента. Встроенные скрипты почти наверняка отсутствуют. IFrames, вероятно, будут удалены. Это, вероятно, просто оставляет теги изображений.

стайлинг

Есть несколько способов подойти к стилю; возможно, вы хотите контролировать все, сохраняя свои значки одинаковыми на всех сайтах. В качестве альтернативы вы могли бы предоставить стили по умолчанию, но позволить владельцам сайтов гибко переопределять их, чтобы лучше соответствовать дизайну их сайтов.

Очевидно, изображения не могут быть настроены; кроме, возможно, их размеров. Если вы используете iframes, стоит отметить, что любые стили, примененные к родительской странице, не будут наследоваться вашим контентом. С другой стороны, если вы используете подход JavaScript, вполне возможно переопределить стиль в зависимости от специфики и того, как вы включаете свои стили. Демонстрационная страница, которая поставляется с примером приложения, показывает это в действии.

настройка

Возможно, подобно Stackoverflow и их значкам «User Flair», вы хотите предоставить ряд альтернативных стилей — например, светлых и темных. Это вполне возможно с любым из описанных мною подходов, хотя, возможно, это немного сложнее с подходом тега изображения.

продвинутый

До сих пор наш встраиваемый контент создавался динамически, но никоим образом не интерактивно. Например, кнопка «Мне нравится» на Facebook не только предоставляет счетчик, но и позволяет людям выполнять действие «Мне нравится» на странице. Такой тип интерактивности будет рассмотрен в следующей статье.

Резюме

Встраиваемый контент — отличный способ продвижения вашего сайта. Его можно использовать не только для ссылки на ваш сайт, но и для предоставления «живого» контента, прямо на стороннем «хостовом» сайте.

Мы рассмотрели три общих подхода к этому — изображения, фреймы и JavaScript. Мы рассмотрели некоторые вещи, о которых вам следует подумать при принятии решения о том, какой из них использовать, а также некоторые подводные камни, о которых следует опасаться.