2022-11-29 14:17:30 +00:00
< ? php
namespace App\Service ;
use App\Models\Forms\Form ;
use App\Models\Template ;
use Illuminate\Http\Request ;
use Illuminate\Routing\Route ;
use Illuminate\Support\Facades\Cache ;
use Illuminate\Support\Str ;
/**
* Generates meta per - route matching . This is useful because Google , Twitter and Facebook struggle to load meta tags
* injected dynamically by JavaScript . This class allows us to inject meta tags in the HTML head tag .
*
* Here ' s how to use this class
* - Add a pattern to URL_PATTERNS
* - Then choose between a static meta or a dynamic meta :
* - If the content is dynamic ( ex : needs to retrieve data from the database ), then add a method to this class for
* the corresponding pattern . The method should be named " getMyPatternName " ( where pattern name is
* my_pattern_name ) and it should return an array of meta tags .
* - If the content is static , then add meta tags to the PATTERN_STATIC_META array .
*/
class SeoMetaResolver
{
private array $patternData = [];
2024-02-23 10:54:12 +00:00
public const URL_PATTERNS = [
2022-11-29 14:17:30 +00:00
'welcome' => '/' ,
'form_show' => '/forms/{slug}' ,
'login' => '/login' ,
'register' => '/register' ,
'reset_password' => '/password/reset' ,
'privacy_policy' => '/privacy-policy' ,
'terms_conditions' => '/terms-conditions' ,
'integrations' => '/integrations' ,
2023-09-08 11:00:28 +00:00
'templates' => '/form-templates' ,
'templates_show' => '/form-templates/{slug}' ,
2023-10-24 18:55:15 +00:00
'templates_types_show' => '/form-templates/types/{slug}' ,
'templates_industries_show' => '/form-templates/industries/{slug}' ,
2022-11-29 14:17:30 +00:00
];
/**
* Metas for simple route ( without needing to access DB )
*/
2024-02-23 10:54:12 +00:00
public const PATTERN_STATIC_META = [
2022-11-29 14:17:30 +00:00
'login' => [
'title' => 'Login' ,
],
'register' => [
'title' => 'Create your account' ,
],
'reset_password' => [
'title' => 'Reset your password' ,
],
'privacy_policy' => [
'title' => 'Our Privacy Policy' ,
],
'terms_conditions' => [
'title' => 'Our Terms & Conditions' ,
],
'integrations' => [
'title' => 'Our Integrations' ,
],
'templates' => [
'title' => 'Templates' ,
2024-02-23 10:54:12 +00:00
'description' => 'Our collection of beautiful templates to create your own forms!' ,
2022-11-29 14:17:30 +00:00
],
];
2024-02-23 10:54:12 +00:00
public const META_CACHE_DURATION = 60 * 60 * 12 ; // 12 hours
2022-11-29 14:17:30 +00:00
2024-02-23 10:54:12 +00:00
public const META_CACHE_KEY_PREFIX = 'seo_meta_' ;
2022-11-29 14:17:30 +00:00
public function __construct ( private Request $request )
{
}
/**
* Returns the right metas for a given route , caches meta for 1 hour .
*/
public function getMetas () : array
{
2024-02-23 10:54:12 +00:00
$cacheKey = self :: META_CACHE_KEY_PREFIX . urlencode ( $this -> request -> path ());
2022-11-29 14:17:30 +00:00
return Cache :: remember ( $cacheKey , now () -> addSeconds ( self :: META_CACHE_DURATION ), function () {
$pattern = $this -> resolvePattern ();
if ( $this -> hasPatternMetaGetter ( $pattern )) {
// Custom function for pattern
try {
2024-02-23 10:54:12 +00:00
return array_merge ( $this -> getDefaultMeta (), $this -> { 'get' . Str :: studly ( $pattern ) . 'Meta' }());
2022-11-29 14:17:30 +00:00
} catch ( \Exception $e ) {
return $this -> getDefaultMeta ();
}
} elseif ( in_array ( $pattern , array_keys ( self :: PATTERN_STATIC_META ))) {
// Simple meta for pattern
$meta = self :: PATTERN_STATIC_META [ $pattern ];
if ( isset ( $meta [ 'title' ])) {
$meta [ 'title' ] .= $this -> titleSuffix ();
}
if ( isset ( $meta [ 'image' ])) {
$meta [ 'image' ] = asset ( $meta [ 'image' ]);
}
return array_merge ( $this -> getDefaultMeta (), $meta );
}
return $this -> getDefaultMeta ();
});
}
/**
* Simulates the Laravel router to match route with Metas
*
* @ return string
*/
private function resolvePattern ()
{
foreach ( self :: URL_PATTERNS as $patternName => $patternData ) {
$path = rtrim ( $this -> request -> getPathInfo (), '/' ) ? : '/' ;
2024-02-23 10:54:12 +00:00
$route = ( new Route ( 'GET' , $patternData , fn () => '' )) -> bind ( $this -> request );
2022-11-29 14:17:30 +00:00
if ( preg_match ( $route -> getCompiled () -> getRegex (), rawurldecode ( $path ))) {
$this -> patternData = $route -> parameters ();
return $patternName ;
}
}
return 'default' ;
}
/**
* Determine if a get mutator exists for a pattern .
*
2024-02-23 10:54:12 +00:00
* @ param string $key
2022-11-29 14:17:30 +00:00
* @ return bool
*/
private function hasPatternMetaGetter ( $key )
{
2024-02-23 10:54:12 +00:00
return method_exists ( $this , 'get' . Str :: studly ( $key ) . 'Meta' );
2022-11-29 14:17:30 +00:00
}
private function titleSuffix ()
{
2024-02-23 10:54:12 +00:00
return ' · ' . config ( 'app.name' );
2022-11-29 14:17:30 +00:00
}
private function getDefaultMeta () : array
{
return [
2024-02-23 10:54:12 +00:00
'title' => 'Create beautiful forms for free' . $this -> titleSuffix (),
2022-11-29 14:17:30 +00:00
'description' => " Create beautiful forms for free. Unlimited fields, unlimited submissions. It's free and it takes less than 1 minute to create your first form. " ,
'image' => asset ( '/img/social-preview.jpg' ),
];
}
private function getFormShowMeta () : array
{
$form = Form :: whereSlug ( $this -> patternData [ 'slug' ]) -> firstOrFail ();
2023-08-30 09:43:11 +00:00
$meta = [];
if ( $form -> is_pro && $form -> seo_meta -> page_title ) {
$meta [ 'title' ] = $form -> seo_meta -> page_title ;
} else {
2024-02-23 10:54:12 +00:00
$meta [ 'title' ] = $form -> title . $this -> titleSuffix ();
2023-08-30 09:43:11 +00:00
}
if ( $form -> is_pro && $form -> seo_meta -> page_description ) {
$meta [ 'description' ] = $form -> seo_meta -> page_description ;
2024-02-23 10:54:12 +00:00
} elseif ( $form -> description ) {
2023-01-05 14:16:06 +00:00
$meta [ 'description' ] = Str :: of ( $form -> description ) -> limit ( 160 );
}
2023-08-30 09:43:11 +00:00
if ( $form -> is_pro && $form -> seo_meta -> page_thumbnail ) {
$meta [ 'image' ] = $form -> seo_meta -> page_thumbnail ;
2024-02-23 10:54:12 +00:00
} elseif ( $form -> cover_picture ) {
2023-01-05 14:16:06 +00:00
$meta [ 'image' ] = $form -> cover_picture ;
}
2024-02-23 10:54:12 +00:00
2023-01-05 14:16:06 +00:00
return $meta ;
2022-11-29 14:17:30 +00:00
}
2023-09-08 11:00:28 +00:00
private function getTemplatesShowMeta () : array
2022-11-29 14:17:30 +00:00
{
$template = Template :: whereSlug ( $this -> patternData [ 'slug' ]) -> firstOrFail ();
return [
2024-02-23 10:54:12 +00:00
'title' => $template -> name . $this -> titleSuffix (),
'description' => Str :: of ( $template -> short_description ) -> limit ( 140 ) . ' | Customize any template and create your own form in minutes.' ,
'image' => $template -> image_url ,
2022-11-29 14:17:30 +00:00
];
}
2023-10-24 18:55:15 +00:00
private function getTemplatesTypesShowMeta () : array
{
$types = json_decode ( file_get_contents ( resource_path ( 'data/forms/templates/types.json' )), true );
$type = $types [ array_search ( $this -> patternData [ 'slug' ], array_column ( $types , 'slug' ))];
return [
'title' => $type [ 'meta_title' ],
'description' => Str :: of ( $type [ 'meta_description' ]) -> limit ( 140 ),
];
}
private function getTemplatesIndustriesShowMeta () : array
{
$industries = json_decode ( file_get_contents ( resource_path ( 'data/forms/templates/industries.json' )), true );
$industry = $industries [ array_search ( $this -> patternData [ 'slug' ], array_column ( $industries , 'slug' ))];
return [
'title' => $industry [ 'meta_title' ],
'description' => Str :: of ( $industry [ 'meta_description' ]) -> limit ( 140 ),
];
}
2022-11-29 14:17:30 +00:00
}