Skip to content

Bring in Laravel's Blade Templating Engine. #9968

@touhidurabir

Description

@touhidurabir

R&D on a proof of concept to see if it is possible to bring in and integrate the Laravel's blade templating engine into the core system .

Usage Doc

  • By default all the views in pkp share lib and app level are namespaced but it will work with/without namespace . For example , @include('VIEW_NAMESPACE::some-template') or just @include('some-template') . However without specifying the namespace , the app level view will have higher priority when exists .
  • Also by default all view components in pkp share lib and app level are namespaced but it will work with/without namespace . For example, <x-COMPONENT_NAMESPACE::some-component /> or just <x-some-component />. However without specifying the namespace , the app level view will have higher priority when exists .
  • The app level view namespace is set to app and shared lib pkp level view namespace is set as pkp. so for view it can work as app::some-template or x-app::some-component.
  • both the .blade and .blade.php extension for blade view files are supported .
  • The default path of component classes are classes/view/components/ and corresponding views are classes/view/components/ for both the app and shared lib pkp level .
  • for plugins , the default path of component classes are classes/components/ and corresponding views are templates/components/.
  • Plugins views will be auto namespaced and need to work with view namespace to render the blade views . by default, for each plugin , the view namespace will be default to it's name but can be overridden by having the method public function getTemplateViewNamespace(): string define for each plugin .
  • for plugins , a variable $viewNamespace which hold the view namespace value of that plugins will be auto injected for each blade view except the class based component views. so plugin can use that variable for include partial view like @include("{$viewNamespace}::frontend.components.header") .
  • plugins must use view namespace to use view components as one of following manner
{{-- Directly use the view namespace in the component usage--}}
<x-citationstylelanguageplugin::citation-style 
       :style-id="$citationStyle['id']"
       :title="$citationStyle['title']"
       :params="$citationArgs"
       :paramsJson="$citationArgsJson"
   />
{{-- Or use as dynamic component only where it's possible to use the `$viewNamespace` in a dynamic way--}}
<x-dynamic-component
    :component="$viewNamespace . '::citation-style'"
    :style-id="$citationStyle['id']"
    :title="$citationStyle['title']"
    :params="$citationArgs"
    :paramsJson="$citationArgsJson"
/>
  • For plugin with class based view component, as it's not possible to have the $viewNamespace auto inject in the component class, use following ways to defined the view path
use Illuminate\Support\Facades\View as ViewFacade;

/**
 * Get the view / contents that represent the component.
 */
public function render(): View|Closure|string
{
        // either use view namespace directly or pass it as prop to the class
        // return view("citationstylelanguageplugin::components.citation-style");
        
        // or use the custom macro as follow 
        return view(
            ViewFacade::resolvePluginComponentViewPath(
                $this,
                'components.citation-style'
            )
        );
}
  • Plugin/App can still update/re-register component via Blade::componentNamespace to have different path for components.
  • In the app level , to render/fetch blade view still use the TemplateManager::display or TemplateManager::fetch with first arg passed without any extension . For plugin , use $this->getTemplateResource('blade-view') .
  • Several of smarty plugin/modifier are converted to blade as @loadScript, @loadStylesheet, @loadHeader, @loadMenu, @callHook, @runHook . See the PKPBladeViewServiceProvider::boot to lean more about these .
  • Blade by default escape via {{ }} and to explicitly turn off escaping use , {!! !!} . But to be sure that no unintended stuff get in , better to use {!! ViewHelper::sanitizeHtml($variable) !!}.
  • For most of the string and array level manipulation , Str and Arr can use be used in blade view if compatible method available .

Smarty -> Blade transition examples:

translate

// Smarty
<span>{translate key="navigation.archives"}</span>

// Blade
 <span>{{ __('navigation.archives') }}</span>

date_format

// Smarty
<span>{$firstPublication->getData('datePublished')|date_format:$dateFormatShort}</span>

// Blade
 <span>{{ ViewHelper::dateFormat($firstPublication->getData('datePublished'), $dateFormatShort) }}</span>

url

// Smarty
<a href="{url router=PKP\core\PKPApplication::ROUTE_PAGE page="issue" op="archive"}">
  {translate key="navigation.archives"}
</a>


// Blade
<a href="{{ ViewHelper::url(['router' => \PKP\core\PKPApplication::ROUTE_PAGE, 'page' => 'issue', 'op' => 'archive']) }}">
   {{ __('navigation.archives') }}
 </a>

default

// Smarty
<img alt="{$coverImage.altText|escape|default:''}" >

// Blade
<img alt="{{ $coverImage['altText'] ?? '' }}">

escape

Not needed as Blade escapes by default.

// Smarty
<img src="{$publication->getLocalizedCoverImageUrl($article->getData('contextId'))|escape}">

// Blade
<img src="{{ $publication->getLocalizedCoverImageUrl($article->getData('contextId')) }}">

intval/json_encode/nl2br/parse_str/parse_url/string_tags/strtok

These are php native functions that can be used directly

// Smarty 
<div>{$publication->getData('citationsRaw')|escape|nl2br}</div>

// Blade
 <div>{{ nl2br($publication->getData('citationsRaw')) }}</div>

replace

// Smarty

<html lang="{$currentLocale|replace:"_":"-"}" xml:lang="{$currentLocale|replace:"_":"-"}">

// Blade
<html lang="{{ str_replace('_', '-', $currentLocale) }}" xml:lang="{{ str_replace('_', '-', $currentLocale) }}">

string_format

// Smarty
<td>{$subscriptionType->getCost()|string_format:"%.2f"}&nbsp;({$subscriptionType->getCurrencyStringShort()|escape}</td>

// Blade
<td>{{ sprintf('%.2f', $subscriptionType->getCost()) }}&nbsp;({{ $subscriptionType->getCurrencyStringShort() }})</td>

strip_unsafe_html

// Smarty
<span>{$publication->getLocalizedTitle(null, 'html')|strip_unsafe_html}</span>

// Blade
 <span>{!! ViewHelper::sanitizeHtml($publication->getLocalizedTitle(null, 'html')) !!}</span>

to_array

// Smarty
<a href="{url page="article" op="view" path=$article->getBestId()|to_array:"version":$iPublication->getId()}">{$name}</a>

// Blade
<a href="{{ route('article.view', [$article->getBestId(), 'version', $iPublication->getId()]) }}"> {{ $name }}</a>

PRs
pkp-lib -> #9969
ojs --> pkp/ojs#4277
omp --> pkp/omp#2125
ops --> pkp/ops#1101

citationStyleLanguage --> pkp/citationStyleLanguage#155

ViewHelper enhancement added via merged PR at #11924

Metadata

Metadata

Labels

Enhancement:3:MajorA new feature or improvement that will take a month or more to complete.

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions