Como integrar webmentions usando laravel.
Este artículo tiene más de un año de antigüedad, el contenido pudiera estar obsoleto.
Similar al pingback, Webmention es uno de los cuatro tipos de enlaces de retorno, pero fue diseñado para ser más simple que el protocolo XML-RPC en el que se basa el pingback, y en su lugar solo usa contenido HTTP y x-www-urlencoded. Más allá de los protocolos de enlace de retorno anteriores, Webmention también especifica los detalles del protocolo para cuando una página que es la fuente de un enlace se elimina, se actualiza con nuevos enlaces o se eliminan los enlaces existentes.
Webmention es una recomendación del W3C que describe un protocolo simple para notificar cualquier URL cuando un sitio web se vincula a él, y para que las páginas web soliciten notificaciones cuando alguien se vincula a ellas. Webmention se desarrolló originalmente en la comunidad IndieWebCamp y se publicó como borrador de trabajo del W3C el 12 de enero de 2016. A partir del 12 de enero de 2017, es una recomendación del W3C. Webmention permite a los autores realizar un seguimiento de quién enlaza, hace referencia o comenta sobre sus artículos. Al incorporar dichos comentarios de otros sitios, los propios sitios proporcionan una funcionalidad de comentarios federados.
Conociendo ya de que trata esto de las webmentions me dediqué a buscar como hacerlo funcionar con laravel y encontré un post de Tom Witkowski donde explica como hizo un componente de blade para gestionar sus webmentions. Les recomiendo que vayan y vean el artículo original para que entiendan lo que hizo Tom y comparen con los cambios que yo hice.
Todo el componente es esto:
1<?php 2 3namespace App\View\Components\Front\Webmentions; 4 5use Illuminate\View\Component; 6use Illuminate\Support\Collection; 7use Illuminate\Support\Facades\Http; 8 9class Webmentions extends Component 10{ 11 public Collection $likes; 12 public Collection $reposts; 13 public Collection $comments; 14 15 public function __construct(?string $url = null) 16 { 17 $url ??= request()->url(); 18 19 $webmentions = collect(); 20 21 $page = 0; 22 23 do { 24 $entries = Http::get('https://webmention.io/api/mentions.jf2', [ 25 'token' => config('services.webmention.token'), 26 'domain' => parse_url($url, PHP_URL_HOST), 27 'per-page' => 100, 28 'page' => $page, 29 ])->json()['children'] ?? []; 30 31 $webmentions->push(...$entries); 32 33 $page++; 34 } while (count($entries) >= 100); 35 36 $webmentions = $webmentions 37 ->filter(fn (array $entry): bool => trim(parse_url($entry['wm-target'], PHP_URL_PATH), '/') === trim(parse_url($url, PHP_URL_PATH), '/')); 38 39 $key = 'article_likes_'.$url; 40 41 if (config('app.env') != 'local') { 42 $this->likes = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) { 43 return $this->getLikes($webmentions); 44 }); 45 } 46 47 $key = 'article_reposts_'.$url; 48 49 if (config('app.env') != 'local') { 50 $this->reposts = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) { 51 return $this->getRepost($webmentions); 52 }); 53 } 54 55 $key = 'article_comments_'.$url; 56 57 if (config('app.env') != 'local') { 58 $this->comments = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) { 59 return $this->getComments($webmentions); 60 }); 61 } 62 } 63 64 /** 65 * Get the view / contents that represent the component. 66 * 67 * @return \Illuminate\Contracts\View\View|string 68 */ 69 public function render() 70 { 71 return view('components.front.webmentions.webmentions'); 72 } 73 74 private function getComments($webmentions) 75 { 76 return $webmentions 77 ->filter(fn (array $entry): bool => in_array($entry['wm-property'], ['mention-of', 'in-reply-to'])) 78 ->reject(fn (array $entry): bool => empty($entry['content']['text'])); 79 } 80 81 private function getLikes($webmentions) 82 { 83 return $webmentions 84 ->filter(fn (array $entry): bool => $entry['wm-property'] === 'like-of'); 85 } 86 87 private function getRepost($webmentions) 88 { 89 return $webmentions->filter(function (array $entry): bool { 90 if ($entry['wm-property'] === 'repost-of') { 91 return true; 92 } 93 94 if ($entry['wm-property'] === 'mention-of') { 95 return empty($entry['content']['text']); 96 } 97 98 return false; 99 });100 }101}
Ok, y que fue lo que cambió?
Bueno, básicamente lo que hice fue guardar en cache la respuesta de los "likes", "comments" y "repost" de la siguiente forma:
1$key = 'article_likes_'.$url; 2 3if (config('app.env') != 'local') { 4 $this->likes = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) { 5 return $this->getLikes($webmentions); 6 }); 7} 8 9$key = 'article_reposts_'.$url;10 11if (config('app.env') != 'local') {12 $this->reposts = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) {13 return $this->getRepost($webmentions);14 });15}16 17$key = 'article_comments_'.$url;18 19if (config('app.env') != 'local') {20 $this->comments = \Illuminate\Support\Facades\Cache::remember($key, 21600, function () use ($webmentions) {21 return $this->getComments($webmentions);22 });23}
Tambien hice 3 metodos que se encargan de cada respuesta otorgada por las webmentions
1private function getComments($webmentions) 2{ 3 return $webmentions 4 ->filter(fn (array $entry): bool => in_array($entry['wm-property'], ['mention-of', 'in-reply-to'])) 5 ->reject(fn (array $entry): bool => empty($entry['content']['text'])); 6} 7 8private function getLikes($webmentions) 9{10 return $webmentions11 ->filter(fn (array $entry): bool => $entry['wm-property'] === 'like-of');12}13 14private function getRepost($webmentions)15{16 return $webmentions->filter(function (array $entry): bool {17 if ($entry['wm-property'] === 'repost-of') {18 return true;19 }20 if ($entry['wm-property'] === 'mention-of') {21 return empty($entry['content']['text']);22 }23 return false;24 });25}
En líneas generales lo que hice fue ordenar un poco el componente inicial creado por Tom y lo adapté a como quería.
Luego haré un post de como mostrar todo esto usando otro componente ;)