<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-310346853756243855</atom:id><lastBuildDate>Tue, 05 May 2026 09:11:38 +0000</lastBuildDate><category>CodeIgniter</category><category>Magento</category><category>Joomla</category><category>CakePHP</category><category>Drupal</category><category>Wordpress</category><category>AI</category><category>Symfony</category><category>YII</category><category>Zend</category><category>Laravel</category><title>PHP CMS Framework</title><description>Welcome to PHP CMS Framework - Your premier destination for PHP content management system expertise and resources. Dive into our comprehensive tutorials, articles, and guides tailored to both novice developers and seasoned experts. Join our vibrant community of PHP enthusiasts and discover the latest insights, techniques, and best practices. Whether you&#39;re mastering PHP frameworks like Laravel and Symfony or exploring the intricacies of popular CMS platforms like WordPress and Drupal.</description><link>http://www.phpcmsframework.com/</link><managingEditor>noreply@blogger.com (PHP CMS Frameworks)</managingEditor><generator>Blogger</generator><openSearch:totalResults>203</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-1064998400045308483</guid><pubDate>Sun, 26 Apr 2026 13:31:00 +0000</pubDate><atom:updated>2026-04-26T06:31:36.488-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Joomla</category><title>Build an AI-Driven personalisation engine in Joomla using User Behaviour data</title><description>&lt;p&gt;
Most Joomla sites serve the same content to every registered user regardless of what they have read before, what they searched for, or how long they spent on specific topics. A user who has read six articles about Joomla security gets the same homepage recommendations as someone who only reads performance tuning content. That is a missed opportunity every single time they log in.
&lt;/p&gt;

&lt;p&gt;
Personalisation is not a new idea but it has historically required either expensive third-party platforms or a data science team to implement properly. What has changed is that OpenAI&#39;s embedding and completion APIs make it possible to build a genuinely useful personalisation engine inside your existing Joomla installation without either of those things.
&lt;/p&gt;

&lt;p&gt;
What we are building here is a system that tracks what registered users click on, which articles they read, how long they spend reading, and what they search for. It uses that behaviour data to build an interest profile per user, then uses OpenAI embeddings to find articles that match that profile semantically, not just by category tag. The result is a recommendations module that gets more accurate the more a user engages with the site.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you need:&lt;/strong&gt; Joomla 4 or 5, PHP 8.1+, Composer, MySQL, and an OpenAI API key.&lt;/p&gt;


&lt;h2&gt;How the system works End to End&lt;/h2&gt;

&lt;p&gt;
Before writing any code, the architecture is worth understanding clearly. There are three distinct parts to this system and keeping them separate makes the whole thing easier to build and maintain.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Part 1: Behaviour Tracking
        User reads an article, searches, or clicks a link
                ↓
        JavaScript sends event data to a Joomla plugin endpoint
                ↓
        Event stored in user_behaviour_events table

Part 2: Profile Building (runs on cron every hour)
        Fetch recent behaviour events per user
                ↓
        Build a text summary of user interests from event data
                ↓
        Send summary to OpenAI Embeddings API
                ↓
        Store interest profile vector in user_interest_profiles table

Part 3: Recommendations (runs on module render)
        Load current user&#39;s interest profile vector
                ↓
        Compare against pre-computed article embedding vectors
                ↓
        Return top N articles by cosine similarity
                ↓
        Render as a recommendations module on any page&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The profile building step is the key insight here. Rather than trying to match individual behaviour events to articles directly, we build a text summary of each user&#39;s interests from their behaviour data, embed that summary as a vector, and then find articles that are semantically close to it. This means a user who reads articles about &quot;Joomla template overrides&quot; and &quot;child theme development&quot; will get recommendations about &quot;Joomla layout XML&quot; even if that exact phrase never appeared in their behaviour history.
&lt;/p&gt;


&lt;h2&gt;Database Tables&lt;/h2&gt;

&lt;p&gt;
We need three tables. One for raw behaviour events, one for computed user interest profiles, and one for pre-computed article embeddings. Run these in your Joomla database:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE `#__user_behaviour_events` (
    `id`           INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `user_id`      INT UNSIGNED NOT NULL,
    `event_type`   ENUM(&#39;view&#39;,&#39;click&#39;,&#39;search&#39;,&#39;time_on_page&#39;) NOT NULL,
    `article_id`   INT UNSIGNED NULL,
    `search_query` VARCHAR(500) NULL,
    `duration_seconds` INT UNSIGNED NULL,
    `metadata`     JSON NULL,
    `created_at`   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    INDEX `idx_user_events` (`user_id`, `created_at`),
    INDEX `idx_article_events` (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `#__user_interest_profiles` (
    `user_id`      INT UNSIGNED PRIMARY KEY,
    `interest_summary` TEXT NOT NULL,
    `embedding`    JSON NOT NULL,
    `events_count` INT UNSIGNED NOT NULL DEFAULT 0,
    `updated_at`   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `#__article_embeddings` (
    `article_id`   INT UNSIGNED PRIMARY KEY,
    `embedding`    JSON NOT NULL,
    `indexed_at`   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
MySQL does not have a native vector type so we are storing embeddings as JSON arrays. For sites with large article catalogues, over a few thousand articles, consider migrating to PostgreSQL with pgvector for proper vector indexing and faster similarity queries. For most Joomla sites MySQL with JSON embeddings works fine.
&lt;/p&gt;


&lt;h2&gt;Part 1: Behaviour Tracking Plugin&lt;/h2&gt;

&lt;p&gt;
Create a Joomla system plugin that handles two things: serving a lightweight JavaScript tracker, and receiving the behaviour events that tracker sends back.
&lt;/p&gt;

&lt;p&gt;Module structure at &lt;code&gt;plugins/system/behaviourtracker/&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;plugins/system/behaviourtracker/
    behaviourtracker.php
    behaviourtracker.xml
    src/
        Extension/
            BehaviourTracker.php
    media/
        js/
            tracker.js&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The main plugin class at &lt;code&gt;src/Extension/BehaviourTracker.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Plugin\System\BehaviourTracker\Extension;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;

class BehaviourTracker extends CMSPlugin
{
    public function onAfterDispatch(): void
    {
        $app  = Factory::getApplication();
        $user = Factory::getUser();

        // Only track logged-in users on site pages
        if ($user-&amp;gt;guest || $app-&amp;gt;isClient(&#39;administrator&#39;)) {
            return;
        }

        $input  = $app-&amp;gt;getInput();
        $option = $input-&amp;gt;getCmd(&#39;option&#39;);
        $view   = $input-&amp;gt;getCmd(&#39;view&#39;);

        // Track article views automatically on the server side
        if ($option === &#39;com_content&#39; &amp;amp;&amp;amp; $view === &#39;article&#39;) {
            $articleId = $input-&amp;gt;getInt(&#39;id&#39;);
            if ($articleId) {
                $this-&amp;gt;recordEvent($user-&amp;gt;id, &#39;view&#39;, $articleId);
            }
        }

        // Inject the JavaScript tracker on all frontend pages
        $doc     = $app-&amp;gt;getDocument();
        $baseUrl = Uri::root();

        $doc-&amp;gt;addScriptOptions(&#39;behaviourTracker&#39;, [
            &#39;userId&#39;   =&amp;gt; $user-&amp;gt;id,
            &#39;endpoint&#39; =&amp;gt; $baseUrl . &#39;index.php?option=com_ajax&amp;group=system&amp;plugin=behaviourtracker&amp;format=json&#39;,
            &#39;articleId&#39;=&amp;gt; ($option === &#39;com_content&#39; &amp;amp;&amp;amp; $view === &#39;article&#39;)
                          ? $input-&amp;gt;getInt(&#39;id&#39;) : null,
        ]);

        $doc-&amp;gt;addScript(Uri::root() . &#39;media/plg_system_behaviourtracker/js/tracker.js&#39;, [], [&#39;defer&#39; =&amp;gt; true]);
    }

    public function onAjaxBehaviourtracker(): void
    {
        $app  = Factory::getApplication();
        $user = Factory::getUser();

        if ($user-&amp;gt;guest) {
            echo json_encode([&#39;status&#39; =&amp;gt; &#39;ignored&#39;]);
            $app-&amp;gt;close();
        }

        $input     = $app-&amp;gt;getInput()-&amp;gt;json;
        $eventType = $input-&amp;gt;getString(&#39;event_type&#39;);
        $articleId = $input-&amp;gt;getInt(&#39;article_id&#39;, 0);
        $query     = $input-&amp;gt;getString(&#39;search_query&#39;, &#39;&#39;);
        $duration  = $input-&amp;gt;getInt(&#39;duration_seconds&#39;, 0);

        $allowed = [&#39;click&#39;, &#39;search&#39;, &#39;time_on_page&#39;];

        if (!in_array($eventType, $allowed)) {
            echo json_encode([&#39;status&#39; =&amp;gt; &#39;invalid&#39;]);
            $app-&amp;gt;close();
        }

        $this-&amp;gt;recordEvent(
            $user-&amp;gt;id,
            $eventType,
            $articleId ?: null,
            $query ?: null,
            $duration ?: null
        );

        echo json_encode([&#39;status&#39; =&amp;gt; &#39;ok&#39;]);
        $app-&amp;gt;close();
    }

    private function recordEvent(
        int     $userId,
        string  $eventType,
        ?int    $articleId   = null,
        ?string $searchQuery = null,
        ?int    $duration    = null
    ): void {
        $db = Factory::getDbo();

        $row = (object)[
            &#39;user_id&#39;          =&amp;gt; $userId,
            &#39;event_type&#39;       =&amp;gt; $eventType,
            &#39;article_id&#39;       =&amp;gt; $articleId,
            &#39;search_query&#39;     =&amp;gt; $searchQuery,
            &#39;duration_seconds&#39; =&amp;gt; $duration,
            &#39;created_at&#39;       =&amp;gt; date(&#39;Y-m-d H:i:s&#39;),
        ];

        $db-&amp;gt;insertObject(&#39;#__user_behaviour_events&#39;, $row);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now the JavaScript tracker at &lt;code&gt;media/js/tracker.js&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
    const config = Joomla.getOptions(&#39;behaviourTracker&#39;);

    if (!config || !config.userId) return;

    const endpoint = config.endpoint;
    const articleId = config.articleId;

    function send(payload) {
        navigator.sendBeacon
            ? navigator.sendBeacon(endpoint, JSON.stringify(payload))
            : fetch(endpoint, { method: &#39;POST&#39;, body: JSON.stringify(payload),
                                keepalive: true });
    }

    // Track time on page for article views
    if (articleId) {
        const startTime = Date.now();

        window.addEventListener(&#39;beforeunload&#39;, function () {
            const duration = Math.round((Date.now() - startTime) / 1000);

            // Only record if they spent more than 10 seconds
            if (duration &amp;gt; 10) {
                send({
                    event_type:       &#39;time_on_page&#39;,
                    article_id:       articleId,
                    duration_seconds: duration,
                });
            }
        });
    }

    // Track internal link clicks to articles
    document.addEventListener(&#39;click&#39;, function (e) {
        const link = e.target.closest(&#39;a[href]&#39;);
        if (!link) return;

        const href = link.getAttribute(&#39;href&#39;);
        const match = href.match(/[?&amp;]id=(\d+)/);

        if (match &amp;&amp; href.includes(&#39;com_content&#39;)) {
            send({
                event_type: &#39;click&#39;,
                article_id: parseInt(match[1]),
            });
        }
    });

    // Track search queries
    const searchForm = document.querySelector(&#39;form[action*=&quot;com_search&quot;], form[action*=&quot;com_finder&quot;]&#39;);

    if (searchForm) {
        searchForm.addEventListener(&#39;submit&#39;, function () {
            const input = searchForm.querySelector(&#39;input[type=&quot;text&quot;], input[name=&quot;q&quot;], input[name=&quot;searchword&quot;]&#39;);

            if (input &amp;amp;&amp;amp; input.value.trim().length &amp;gt; 2) {
                send({
                    event_type:   &#39;search&#39;,
                    search_query: input.value.trim(),
                });
            }
        });
    }
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Using &lt;code&gt;navigator.sendBeacon&lt;/code&gt; for the time-on-page event is important. When a user navigates away, &lt;code&gt;fetch&lt;/code&gt; requests get cancelled before they complete. &lt;code&gt;sendBeacon&lt;/code&gt; is designed specifically for this situation and guarantees the request goes through even as the page unloads.
&lt;/p&gt;


&lt;h2&gt;Part 2: Interest Profile Builder&lt;/h2&gt;

&lt;p&gt;
This is the part that makes the recommendations smart rather than just &quot;articles in the same category.&quot; Create this as a Joomla CLI task that runs on cron every hour.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;components/com_personalisation/src/Service/ProfileBuilder.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Personalisation\Site\Service;

use Joomla\CMS\Factory;
use OpenAI;

class ProfileBuilder
{
    private $openai;
    private int $lookbackDays = 30;
    private int $minEvents    = 3;

    public function __construct()
    {
        $params       = \JComponentHelper::getParams(&#39;com_personalisation&#39;);
        $this-&amp;gt;openai = OpenAI::client($params-&amp;gt;get(&#39;openai_api_key&#39;));
    }

    public function buildForAllUsers(): array
    {
        $db = Factory::getDbo();

        // Find users who have behaviour events and need profile updates
        $query = $db-&amp;gt;getQuery(true)
            -&amp;gt;select(&#39;DISTINCT e.user_id&#39;)
            -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__user_behaviour_events&#39;, &#39;e&#39;))
            -&amp;gt;where($db-&amp;gt;quoteName(&#39;e.created_at&#39;) . &#39; &amp;gt; &#39; .
                    $db-&amp;gt;quote(date(&#39;Y-m-d H:i:s&#39;, strtotime(&quot;-{$this-&amp;gt;lookbackDays} days&quot;))))
            -&amp;gt;group($db-&amp;gt;quoteName(&#39;e.user_id&#39;))
            -&amp;gt;having(&#39;COUNT(*) &amp;gt;= &#39; . $this-&amp;gt;minEvents);

        $userIds = $db-&amp;gt;setQuery($query)-&amp;gt;loadColumn();

        $built  = 0;
        $errors = 0;

        foreach ($userIds as $userId) {
            try {
                $this-&amp;gt;buildForUser((int) $userId);
                $built++;
            } catch (\Exception $e) {
                $errors++;
                \JLog::add(
                    &quot;Profile build failed for user {$userId}: &quot; . $e-&amp;gt;getMessage(),
                    \JLog::ERROR,
                    &#39;com_personalisation&#39;
                );
            }

            // Avoid hitting OpenAI rate limits
            usleep(100000);
        }

        return [&#39;built&#39; =&amp;gt; $built, &#39;errors&#39; =&amp;gt; $errors];
    }

    public function buildForUser(int $userId): void
    {
        $db     = Factory::getDbo();
        $events = $this-&amp;gt;fetchEvents($userId);

        if (empty($events)) {
            return;
        }

        // Build interest summary from behaviour events
        $summary = $this-&amp;gt;buildInterestSummary($userId, $events);

        // Get embedding for the interest summary
        $response  = $this-&amp;gt;openai-&amp;gt;embeddings()-&amp;gt;create([
            &#39;model&#39; =&amp;gt; &#39;text-embedding-3-small&#39;,
            &#39;input&#39; =&amp;gt; $summary,
        ]);

        $embedding = $response-&amp;gt;embeddings[0]-&amp;gt;embedding;

        // Upsert the user interest profile
        $existing = $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select(&#39;user_id&#39;)
                -&amp;gt;from(&#39;#__user_interest_profiles&#39;)
                -&amp;gt;where(&#39;user_id = &#39; . (int) $userId)
        )-&amp;gt;loadResult();

        $row = (object)[
            &#39;user_id&#39;          =&amp;gt; $userId,
            &#39;interest_summary&#39; =&amp;gt; $summary,
            &#39;embedding&#39;        =&amp;gt; json_encode($embedding),
            &#39;events_count&#39;     =&amp;gt; count($events),
            &#39;updated_at&#39;       =&amp;gt; date(&#39;Y-m-d H:i:s&#39;),
        ];

        $existing
            ? $db-&amp;gt;updateObject(&#39;#__user_interest_profiles&#39;, $row, &#39;user_id&#39;)
            : $db-&amp;gt;insertObject(&#39;#__user_interest_profiles&#39;, $row);
    }

    private function fetchEvents(int $userId): array
    {
        $db    = Factory::getDbo();
        $since = date(&#39;Y-m-d H:i:s&#39;, strtotime(&quot;-{$this-&amp;gt;lookbackDays} days&quot;));

        $query = $db-&amp;gt;getQuery(true)
            -&amp;gt;select([
                &#39;e.event_type&#39;,
                &#39;e.article_id&#39;,
                &#39;e.search_query&#39;,
                &#39;e.duration_seconds&#39;,
                &#39;a.title AS article_title&#39;,
                &#39;a.catid&#39;,
                &#39;c.title AS category_title&#39;,
            ])
            -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__user_behaviour_events&#39;, &#39;e&#39;))
            -&amp;gt;leftJoin($db-&amp;gt;quoteName(&#39;#__content&#39;, &#39;a&#39;) . &#39; ON a.id = e.article_id&#39;)
            -&amp;gt;leftJoin($db-&amp;gt;quoteName(&#39;#__categories&#39;, &#39;c&#39;) . &#39; ON c.id = a.catid&#39;)
            -&amp;gt;where(&#39;e.user_id = &#39; . (int) $userId)
            -&amp;gt;where(&#39;e.created_at &amp;gt; &#39; . $db-&amp;gt;quote($since))
            -&amp;gt;order(&#39;e.created_at DESC&#39;)
            -&amp;gt;setLimit(200);

        return $db-&amp;gt;setQuery($query)-&amp;gt;loadAssocList();
    }

    private function buildInterestSummary(int $userId, array $events): string
    {
        $viewedTitles    = [];
        $searchQueries   = [];
        $categories      = [];
        $longReads       = [];

        foreach ($events as $event) {
            if ($event[&#39;article_title&#39;]) {
                $viewedTitles[] = $event[&#39;article_title&#39;];
            }

            if ($event[&#39;category_title&#39;]) {
                $categories[] = $event[&#39;category_title&#39;];
            }

            if ($event[&#39;search_query&#39;]) {
                $searchQueries[] = $event[&#39;search_query&#39;];
            }

            // Articles read for more than 90 seconds indicate strong interest
            if ($event[&#39;duration_seconds&#39;] &amp;gt; 90 &amp;amp;&amp;amp; $event[&#39;article_title&#39;]) {
                $longReads[] = $event[&#39;article_title&#39;];
            }
        }

        // Deduplicate and limit to avoid overly long summaries
        $viewedTitles  = array_unique(array_slice($viewedTitles, 0, 20));
        $searchQueries = array_unique(array_slice($searchQueries, 0, 10));
        $categories    = array_values(array_unique($categories));
        $longReads     = array_unique($longReads);

        // Count category frequency to identify dominant interests
        $catCounts = array_count_values(array_column($events, &#39;category_title&#39;));
        arsort($catCounts);
        $topCategories = array_keys(array_slice($catCounts, 0, 5, true));

        $parts = [];

        if (!empty($topCategories)) {
            $parts[] = &quot;Primary interests: &quot; . implode(&#39;, &#39;, $topCategories) . &quot;.&quot;;
        }

        if (!empty($searchQueries)) {
            $parts[] = &quot;Searched for: &quot; . implode(&#39;, &#39;, $searchQueries) . &quot;.&quot;;
        }

        if (!empty($longReads)) {
            $parts[] = &quot;Read thoroughly: &quot; . implode(&#39;, &#39;, $longReads) . &quot;.&quot;;
        }

        if (!empty($viewedTitles)) {
            $parts[] = &quot;Also viewed: &quot; . implode(&#39;, &#39;, $viewedTitles) . &quot;.&quot;;
        }

        return implode(&#39; &#39;, $parts);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The interest summary construction is worth explaining. We weight long reads (over 90 seconds) separately because time spent reading is a stronger signal of genuine interest than a quick click. Category frequency tells us which topics dominate a user&#39;s browsing. Search queries tell us what they were actively looking for, not just passively browsing. Combining these three signals produces a richer interest summary than article titles alone.
&lt;/p&gt;


&lt;h2&gt;Part 3: Article Indexer&lt;/h2&gt;

&lt;p&gt;
Before we can recommend articles, we need embeddings for each article. This service indexes your Joomla content into the article embeddings table.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;components/com_personalisation/src/Service/ArticleIndexer.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Personalisation\Site\Service;

use Joomla\CMS\Factory;
use OpenAI;

class ArticleIndexer
{
    private $openai;
    private int $batchSize = 20;

    public function __construct()
    {
        $params       = \JComponentHelper::getParams(&#39;com_personalisation&#39;);
        $this-&amp;gt;openai = OpenAI::client($params-&amp;gt;get(&#39;openai_api_key&#39;));
    }

    public function indexAll(bool $forceReindex = false): array
    {
        $db = Factory::getDbo();

        $query = $db-&amp;gt;getQuery(true)
            -&amp;gt;select([&#39;a.id&#39;, &#39;a.title&#39;, &#39;a.introtext&#39;, &#39;a.fulltext&#39;, &#39;c.title AS category&#39;])
            -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__content&#39;, &#39;a&#39;))
            -&amp;gt;leftJoin($db-&amp;gt;quoteName(&#39;#__categories&#39;, &#39;c&#39;) . &#39; ON c.id = a.catid&#39;)
            -&amp;gt;where(&#39;a.state = 1&#39;);

        if (!$forceReindex) {
            // Only index articles without existing embeddings
            $query-&amp;gt;leftJoin($db-&amp;gt;quoteName(&#39;#__article_embeddings&#39;, &#39;ae&#39;) . &#39; ON ae.article_id = a.id&#39;)
                  -&amp;gt;where(&#39;ae.article_id IS NULL&#39;);
        }

        $articles = $db-&amp;gt;setQuery($query)-&amp;gt;loadObjectList();

        if (empty($articles)) {
            return [&#39;indexed&#39; =&amp;gt; 0, &#39;skipped&#39; =&amp;gt; 0];
        }

        $indexed = 0;
        $errors  = 0;
        $batches = array_chunk($articles, $this-&amp;gt;batchSize);

        foreach ($batches as $batch) {
            $texts = array_map(function ($article) {
                $body = strip_tags($article-&amp;gt;introtext . &#39; &#39; . $article-&amp;gt;fulltext);
                $body = preg_replace(&#39;/\s+/&#39;, &#39; &#39;, $body);
                $body = substr(trim($body), 0, 2000);
                return $article-&amp;gt;category . &#39;: &#39; . $article-&amp;gt;title . &#39;. &#39; . $body;
            }, $batch);

            try {
                $response = $this-&amp;gt;openai-&amp;gt;embeddings()-&amp;gt;create([
                    &#39;model&#39; =&amp;gt; &#39;text-embedding-3-small&#39;,
                    &#39;input&#39; =&amp;gt; $texts,
                ]);

                foreach ($batch as $i =&amp;gt; $article) {
                    $embedding = $response-&amp;gt;embeddings[$i]-&amp;gt;embedding ?? null;

                    if (!$embedding) {
                        $errors++;
                        continue;
                    }

                    $this-&amp;gt;upsertEmbedding($article-&amp;gt;id, $embedding);
                    $indexed++;
                }

                usleep(200000);

            } catch (\Exception $e) {
                $errors += count($batch);
                \JLog::add(&#39;Article indexing error: &#39; . $e-&amp;gt;getMessage(), \JLog::ERROR, &#39;com_personalisation&#39;);
            }
        }

        return [&#39;indexed&#39; =&amp;gt; $indexed, &#39;errors&#39; =&amp;gt; $errors];
    }

    private function upsertEmbedding(int $articleId, array $embedding): void
    {
        $db = Factory::getDbo();

        $existing = $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select(&#39;article_id&#39;)
                -&amp;gt;from(&#39;#__article_embeddings&#39;)
                -&amp;gt;where(&#39;article_id = &#39; . $articleId)
        )-&amp;gt;loadResult();

        $row = (object)[
            &#39;article_id&#39;  =&amp;gt; $articleId,
            &#39;embedding&#39;   =&amp;gt; json_encode($embedding),
            &#39;indexed_at&#39;  =&amp;gt; date(&#39;Y-m-d H:i:s&#39;),
        ];

        $existing
            ? $db-&amp;gt;updateObject(&#39;#__article_embeddings&#39;, $row, &#39;article_id&#39;)
            : $db-&amp;gt;insertObject(&#39;#__article_embeddings&#39;, $row);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Notice the article text is prefixed with its category title before embedding. &quot;Joomla Performance: Why your Joomla site is slow and how to fix it&quot; produces a more accurate embedding than the title alone because the category provides context that helps distinguish articles on similar topics across different subject areas.
&lt;/p&gt;


&lt;h2&gt;Part 4: The Recommendation Engine&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;components/com_personalisation/src/Service/RecommendationEngine.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Personalisation\Site\Service;

use Joomla\CMS\Factory;

class RecommendationEngine
{
    private float $similarityThreshold = 0.70;

    public function getRecommendations(int $userId, int $limit = 5): array
    {
        $db = Factory::getDbo();

        // Load the user&#39;s interest profile
        $profile = $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select([&#39;embedding&#39;, &#39;events_count&#39;])
                -&amp;gt;from(&#39;#__user_interest_profiles&#39;)
                -&amp;gt;where(&#39;user_id = &#39; . $userId)
        )-&amp;gt;loadObject();

        if (!$profile) {
            // No profile yet, return popular articles as fallback
            return $this-&amp;gt;getPopularArticles($limit);
        }

        $userVector = json_decode($profile-&amp;gt;embedding, true);

        if (empty($userVector)) {
            return $this-&amp;gt;getPopularArticles($limit);
        }

        // Load all article embeddings
        $articles = $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select([&#39;ae.article_id&#39;, &#39;ae.embedding&#39;, &#39;a.title&#39;, &#39;a.alias&#39;,
                           &#39;a.catid&#39;, &#39;a.introtext&#39;, &#39;a.created&#39;, &#39;a.hits&#39;,
                           &#39;c.title AS category&#39;, &#39;c.alias AS cat_alias&#39;])
                -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__article_embeddings&#39;, &#39;ae&#39;))
                -&amp;gt;join(&#39;INNER&#39;, $db-&amp;gt;quoteName(&#39;#__content&#39;, &#39;a&#39;) . &#39; ON a.id = ae.article_id&#39;)
                -&amp;gt;join(&#39;LEFT&#39;, $db-&amp;gt;quoteName(&#39;#__categories&#39;, &#39;c&#39;) . &#39; ON c.id = a.catid&#39;)
                -&amp;gt;where(&#39;a.state = 1&#39;)
        )-&amp;gt;loadObjectList();

        // Get articles the user has already read so we don&#39;t recommend them again
        $readArticleIds = $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select(&#39;DISTINCT article_id&#39;)
                -&amp;gt;from(&#39;#__user_behaviour_events&#39;)
                -&amp;gt;where(&#39;user_id = &#39; . $userId)
                -&amp;gt;where(&#39;article_id IS NOT NULL&#39;)
                -&amp;gt;where(&quot;event_type IN (&#39;view&#39;, &#39;click&#39;)&quot;)
        )-&amp;gt;loadColumn();

        $readSet = array_flip($readArticleIds);

        // Score each article by cosine similarity to user interest vector
        $scored = [];

        foreach ($articles as $article) {
            // Skip already-read articles
            if (isset($readSet[$article-&amp;gt;article_id])) {
                continue;
            }

            $articleVector = json_decode($article-&amp;gt;embedding, true);

            if (empty($articleVector)) {
                continue;
            }

            $similarity = $this-&amp;gt;cosineSimilarity($userVector, $articleVector);

            if ($similarity &amp;gt;= $this-&amp;gt;similarityThreshold) {
                $scored[] = [
                    &#39;article_id&#39; =&amp;gt; $article-&amp;gt;article_id,
                    &#39;title&#39;      =&amp;gt; $article-&amp;gt;title,
                    &#39;alias&#39;      =&amp;gt; $article-&amp;gt;alias,
                    &#39;category&#39;   =&amp;gt; $article-&amp;gt;category,
                    &#39;cat_alias&#39;  =&amp;gt; $article-&amp;gt;cat_alias,
                    &#39;introtext&#39;  =&amp;gt; strip_tags($article-&amp;gt;introtext),
                    &#39;created&#39;    =&amp;gt; $article-&amp;gt;created,
                    &#39;hits&#39;       =&amp;gt; $article-&amp;gt;hits,
                    &#39;similarity&#39; =&amp;gt; $similarity,
                ];
            }
        }

        // Sort by similarity score, highest first
        usort($scored, fn($a, $b) =&amp;gt; $b[&#39;similarity&#39;] &amp;lt;=&amp;gt; $a[&#39;similarity&#39;]);

        return array_slice($scored, 0, $limit);
    }

    private function cosineSimilarity(array $a, array $b): float
    {
        $dot  = 0.0;
        $magA = 0.0;
        $magB = 0.0;

        $len = min(count($a), count($b));

        for ($i = 0; $i &amp;lt; $len; $i++) {
            $dot  += $a[$i] * $b[$i];
            $magA += $a[$i] ** 2;
            $magB += $b[$i] ** 2;
        }

        $magA = sqrt($magA);
        $magB = sqrt($magB);

        return ($magA * $magB) &amp;gt; 0 ? $dot / ($magA * $magB) : 0.0;
    }

    private function getPopularArticles(int $limit): array
    {
        $db = Factory::getDbo();

        return $db-&amp;gt;setQuery(
            $db-&amp;gt;getQuery(true)
                -&amp;gt;select([&#39;a.id AS article_id&#39;, &#39;a.title&#39;, &#39;a.alias&#39;,
                           &#39;a.introtext&#39;, &#39;a.created&#39;, &#39;a.hits&#39;,
                           &#39;c.title AS category&#39;, &#39;c.alias AS cat_alias&#39;])
                -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__content&#39;, &#39;a&#39;))
                -&amp;gt;leftJoin($db-&amp;gt;quoteName(&#39;#__categories&#39;, &#39;c&#39;) . &#39; ON c.id = a.catid&#39;)
                -&amp;gt;where(&#39;a.state = 1&#39;)
                -&amp;gt;order(&#39;a.hits DESC&#39;)
                -&amp;gt;setLimit($limit)
        )-&amp;gt;loadAssocList();
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The fallback to popular articles for users without a profile yet is important. A new registered user has no behaviour history, so returning nothing or an error is a bad experience. Popular articles are a reasonable default until enough behaviour data accumulates, which typically takes two to three sessions.
&lt;/p&gt;

&lt;p&gt;
Excluding already-read articles from recommendations is something a lot of personalisation implementations skip. There is nothing more frustrating for a user than being recommended an article they read last week. The read set lookup adds minimal overhead and makes the recommendations feel genuinely useful.
&lt;/p&gt;


&lt;h2&gt;Part 5: The Recommendations Module&lt;/h2&gt;

&lt;p&gt;
Create a Joomla module that renders the recommendations anywhere on the site. Module structure at &lt;code&gt;modules/mod_personalised_recommendations/&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;modules/mod_personalised_recommendations/
    mod_personalised_recommendations.php
    mod_personalised_recommendations.xml
    tmpl/
        default.php&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The main module file &lt;code&gt;mod_personalised_recommendations.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

defined(&#39;_JEXEC&#39;) or die;

use Joomla\CMS\Factory;
use Joomla\Component\Personalisation\Site\Service\RecommendationEngine;

$user = Factory::getUser();

if ($user-&amp;gt;guest) {
    return;
}

$limit  = $params-&amp;gt;get(&#39;article_count&#39;, 5);
$engine = new RecommendationEngine();

try {
    $recommendations = $engine-&amp;gt;getRecommendations($user-&amp;gt;id, $limit);
} catch (\Exception $e) {
    \JLog::add(&#39;Recommendations module error: &#39; . $e-&amp;gt;getMessage(), \JLog::ERROR, &#39;mod_personalised_recommendations&#39;);
    $recommendations = [];
}

if (empty($recommendations)) {
    return;
}

require JModuleHelper::getLayoutPath(&#39;mod_personalised_recommendations&#39;, $params-&amp;gt;get(&#39;layout&#39;, &#39;default&#39;));&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The module template at &lt;code&gt;tmpl/default.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php defined(&#39;_JEXEC&#39;) or die; ?&amp;gt;

&amp;lt;div class=&quot;mod-personalised-recommendations&quot;&amp;gt;

    &amp;lt;h3&amp;gt;&amp;lt;?php echo htmlspecialchars($params-&amp;gt;get(&#39;header_text&#39;, &#39;Recommended for You&#39;)); ?&amp;gt;&amp;lt;/h3&amp;gt;

    &amp;lt;ul&amp;gt;
        &amp;lt;?php foreach ($recommendations as $item) : ?&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;a href=&quot;&amp;lt;?php echo JRoute::_(
                    &#39;index.php?option=com_content&amp;view=article&amp;id=&#39; . $item[&#39;article_id&#39;]
                    . &#39;&amp;catid=&#39; . ($item[&#39;catid&#39;] ?? &#39;&#39;)
                ); ?&amp;gt;&quot;&amp;gt;
                    &amp;lt;?php echo htmlspecialchars($item[&#39;title&#39;]); ?&amp;gt;
                &amp;lt;/a&amp;gt;

                &amp;lt;?php if ($params-&amp;gt;get(&#39;show_category&#39;, 1) &amp;amp;&amp;amp; !empty($item[&#39;category&#39;])) : ?&amp;gt;
                    &amp;lt;span class=&quot;article-category&quot;&amp;gt;
                        &amp;lt;?php echo htmlspecialchars($item[&#39;category&#39;]); ?&amp;gt;
                    &amp;lt;/span&amp;gt;
                &amp;lt;?php endif; ?&amp;gt;

                &amp;lt;?php if ($params-&amp;gt;get(&#39;show_intro&#39;, 1) &amp;amp;&amp;amp; !empty($item[&#39;introtext&#39;])) : ?&amp;gt;
                    &amp;lt;p&amp;gt;&amp;lt;?php echo htmlspecialchars(substr(strip_tags($item[&#39;introtext&#39;]), 0, 120)) . &#39;...&#39;; ?&amp;gt;&amp;lt;/p&amp;gt;
                &amp;lt;?php endif; ?&amp;gt;
            &amp;lt;/li&amp;gt;
        &amp;lt;?php endforeach; ?&amp;gt;
    &amp;lt;/ul&amp;gt;

&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Wiring It All Together With Cron&lt;/h2&gt;

&lt;p&gt;
The profile builder needs to run on a schedule. Create a Joomla CLI script at &lt;code&gt;cli/personalisation_cron.php&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

define(&#39;_JEXEC&#39;, 1);
define(&#39;JPATH_BASE&#39;, dirname(__DIR__));

require_once JPATH_BASE . &#39;/includes/defines.php&#39;;
require_once JPATH_BASE . &#39;/includes/framework.php&#39;;

use Joomla\CMS\Factory;
use Joomla\Component\Personalisation\Site\Service\ProfileBuilder;
use Joomla\Component\Personalisation\Site\Service\ArticleIndexer;

$app = Factory::getApplication(&#39;cli&#39;);

$task = $argv[1] ?? &#39;profiles&#39;;

switch ($task) {
    case &#39;index&#39;:
        $indexer = new ArticleIndexer();
        $result  = $indexer-&amp;gt;indexAll();
        echo &quot;Indexed: {$result[&#39;indexed&#39;]}, Errors: {$result[&#39;errors&#39;]}\n&quot;;
        break;

    case &#39;profiles&#39;:
    default:
        $builder = new ProfileBuilder();
        $result  = $builder-&amp;gt;buildForAllUsers();
        echo &quot;Profiles built: {$result[&#39;built&#39;]}, Errors: {$result[&#39;errors&#39;]}\n&quot;;
        break;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add these to your server crontab:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Rebuild user interest profiles every hour
0 * * * * php /path/to/joomla/cli/personalisation_cron.php profiles &gt;&gt; /var/log/joomla_personalisation.log 2&gt;&amp;1

# Index new articles every 6 hours
0 */6 * * * php /path/to/joomla/cli/personalisation_cron.php index &gt;&gt; /var/log/joomla_personalisation.log 2&gt;&amp;1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Run the article indexer manually first before anything else:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;php /path/to/joomla/cli/personalisation_cron.php index&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
This builds embeddings for all your existing articles. Depending on how many articles you have, this might take a few minutes and cost a small amount in OpenAI API tokens. For 500 articles it typically costs less than $0.10 using &lt;code&gt;text-embedding-3-small&lt;/code&gt;.
&lt;/p&gt;


&lt;h2&gt;What the Recommendations Look Like in Practice&lt;/h2&gt;

&lt;p&gt;
Here is a realistic example of what the system produces after a user has been active for a few sessions. The user has read articles about Joomla template development, searched for &quot;override layout XML&quot;, and spent over two minutes reading an article about Joomla child templates.
&lt;/p&gt;

&lt;p&gt;Their interest summary built by the profile builder:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Primary interests: Joomla Development, Joomla Theming.
Searched for: override layout XML, Joomla template child.
Read thoroughly: How to Create a Child Template in Joomla 5.
Also viewed: Joomla Template Overrides Explained, Understanding Joomla Layout XML,
             Joomla Module Chrome Types, Adding Custom CSS to a Joomla Template.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Top recommendations returned by the engine (articles they have not read yet):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[
  {
    &quot;title&quot;: &quot;Joomla 5 Template Positions: A Complete Guide&quot;,
    &quot;category&quot;: &quot;Joomla Development&quot;,
    &quot;similarity&quot;: 0.912
  },
  {
    &quot;title&quot;: &quot;How to Override Joomla Core Templates Without Hacking Core&quot;,
    &quot;category&quot;: &quot;Joomla Development&quot;,
    &quot;similarity&quot;: 0.887
  },
  {
    &quot;title&quot;: &quot;Using Bootstrap 5 Effectively in Custom Joomla Templates&quot;,
    &quot;category&quot;: &quot;Joomla Theming&quot;,
    &quot;similarity&quot;: 0.871
  },
  {
    &quot;title&quot;: &quot;Joomla Module Assignment by Menu Item and User Group&quot;,
    &quot;category&quot;: &quot;Joomla Development&quot;,
    &quot;similarity&quot;: 0.843
  },
  {
    &quot;title&quot;: &quot;Debugging Joomla Layout Issues with the Developer Toolbar&quot;,
    &quot;category&quot;: &quot;Joomla Development&quot;,
    &quot;similarity&quot;: 0.831
  }
]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
All five are relevant, none are articles the user has already read, and the recommendations are semantically accurate even though the user never used the exact phrase &quot;template positions&quot; or &quot;Bootstrap&quot; in their search queries. That is the embedding similarity working correctly.
&lt;/p&gt;


&lt;h2&gt;A Few Things Worth Knowing Before You Deploy&lt;/h2&gt;

&lt;p&gt;
The cosine similarity calculation in PHP is fine for article catalogues up to a few thousand articles. Once you get beyond that, the in-memory comparison of every article embedding against every user profile starts to add up. At that scale, moving the article embeddings to PostgreSQL with pgvector and running the similarity query in the database will keep response times fast.
&lt;/p&gt;

&lt;p&gt;
Privacy is worth thinking about carefully before deploying behaviour tracking. Depending on your jurisdiction, tracking logged-in user behaviour may require disclosure in your privacy policy and potentially explicit consent. At minimum, update your privacy policy to mention that behaviour data is collected to improve content recommendations. For sites with European users, review GDPR requirements around behavioural profiling before going live.
&lt;/p&gt;

&lt;p&gt;
The 30-day lookback window in the profile builder is a reasonable default but worth tuning for your site. On a site where users visit daily, 30 days captures good signal. On a site where users visit monthly, a 90-day window gives more data to work with. Adjust the &lt;code&gt;$lookbackDays&lt;/code&gt; property in &lt;code&gt;ProfileBuilder&lt;/code&gt; to match your site&#39;s typical visit cadence.
&lt;/p&gt;

&lt;p&gt;
Finally, add a simple feedback mechanism if you can. Even a small thumbs up or thumbs down on recommended articles gives you data to validate whether the recommendations are actually landing well with your users. If most users ignore or actively dismiss recommendations, that tells you something about either the similarity threshold, the quality of your interest summaries, or the article content itself. Without that feedback loop, you are flying blind on whether the engine is actually helping.
&lt;/p&gt;
</description><link>http://www.phpcmsframework.com/2026/04/joomla-ai-personalisation-engine-user-behaviour.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-857451060297354967</guid><pubDate>Tue, 07 Apr 2026 04:18:00 +0000</pubDate><atom:updated>2026-04-06T21:18:22.777-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Magento</category><title>Magento 2 - tips and tricks every Developer should know</title><description>&lt;p&gt;
Magento 2 has a reputation for being complicated, and honestly that reputation is earned. The learning curve is steep, the documentation has gaps, and some of its architectural decisions only make sense once you have been burned by the alternative. I have been working with Magento 2 since its early releases and the tips in this post come directly from real projects, real mistakes, and things I wish someone had told me before I spent hours figuring them out the hard way.
&lt;/p&gt;

&lt;p&gt;
This is not a list of things you will find in the official documentation. These are the practical things that make daily Magento 2 development faster, less frustrating, and more maintainable.
&lt;/p&gt;


&lt;h2&gt;Development Workflow Tips&lt;/h2&gt;

&lt;h3&gt;1. Always work in developer mode during development&lt;/h3&gt;

&lt;p&gt;
This sounds obvious but I have seen teams develop in default mode and wonder why their template changes are not showing up. In developer mode, Magento disables most caching, enables full error reporting, and processes frontend assets on the fly. Switch modes from the command line:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;php bin/magento deploy:mode:set developer&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The flip side is that production mode compiles and minifies everything, which is why you must run the full deployment commands before pushing to production. Never deploy to production in developer mode. It is slower and exposes internal errors to visitors.
&lt;/p&gt;

&lt;h3&gt;2. Know your cache types and clear only what you need&lt;/h3&gt;

&lt;p&gt;
Running &lt;code&gt;php bin/magento cache:flush&lt;/code&gt; clears everything and takes time. In most cases during development you only need to clear specific cache types. Clearing just the config cache after a config change, or just the layout cache after a layout XML change, is significantly faster.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Clear only config cache
php bin/magento cache:clean config

# Clear layout and block cache after layout changes
php bin/magento cache:clean layout block_html

# Clear full page cache after template changes
php bin/magento cache:clean full_page

# See all cache types and their status
php bin/magento cache:status&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Learn which cache type corresponds to which kind of change. It will save you minutes every time you test something.
&lt;/p&gt;

&lt;h3&gt;3. Use n98-magerun2 for everything the CLI does not cover&lt;/h3&gt;

&lt;p&gt;
n98-magerun2 is the Swiss Army knife for Magento 2 development. It extends Magento&#39;s CLI with hundreds of useful commands for database management, admin user creation, module management, and much more.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Install globally
composer global require n98/magerun2

# Create an admin user without going through the UI
n98-magerun2 admin:user:create --username=devadmin --email=dev@example.com \
    --password=Dev@12345 --firstname=Dev --lastname=Admin

# Run a database query directly
n98-magerun2 db:query &quot;SELECT * FROM admin_user&quot;

# Open a MySQL console connected to Magento&#39;s database
n98-magerun2 db:console

# Show all configuration values for a path
n98-magerun2 config:store:get catalog/search/*&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The &lt;code&gt;db:console&lt;/code&gt; command alone is worth installing it for. No more hunting for database credentials in &lt;code&gt;env.php&lt;/code&gt; every time you need to run a query.
&lt;/p&gt;

&lt;h3&gt;4. Setup Xdebug properly&lt;/h3&gt;

&lt;p&gt;
Too many Magento developers rely on &lt;code&gt;var_dump&lt;/code&gt; and log files for debugging. Xdebug with step-through debugging changes how you work entirely. You can inspect the full call stack, watch variable values change in real time, and understand exactly what Magento is doing at any point in its request cycle.
&lt;/p&gt;

&lt;p&gt;
The most common Xdebug configuration issue with Magento is the request timeout. Set a generous timeout in your &lt;code&gt;php.ini&lt;/code&gt; because Magento&#39;s bootstrap takes longer than a typical PHP application and Xdebug adds overhead:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal  ; if using Docker
xdebug.client_port=9003
xdebug.max_execution_time=0  ; disable timeout during debug sessions&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;5. Use the Magento 2 console to run cron manually&lt;/h3&gt;

&lt;p&gt;
Waiting for Magento&#39;s cron to run on its schedule during development is painful. You can trigger specific cron groups manually:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Run all cron jobs
php bin/magento cron:run

# Run a specific cron group
php bin/magento cron:run --group=index

# Check cron schedule
php bin/magento cron:schedule&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
If something is not updating after you expect it to, a manual cron run is often the answer.
&lt;/p&gt;


&lt;h2&gt;Performance Tips&lt;/h2&gt;

&lt;h3&gt;6. Enable Redis for Cache and Session Storage&lt;/h3&gt;

&lt;p&gt;
File-based cache storage is fine for development but it will not scale in production. Redis handles Magento&#39;s cache and session storage significantly better, especially under concurrent load. Configure it in &lt;code&gt;app/etc/env.php&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&#39;cache&#39; =&amp;gt; [
    &#39;frontend&#39; =&amp;gt; [
        &#39;default&#39; =&amp;gt; [
            &#39;backend&#39; =&amp;gt; &#39;Magento\\Framework\\Cache\\Backend\\Redis&#39;,
            &#39;backend_options&#39; =&amp;gt; [
                &#39;server&#39;   =&amp;gt; &#39;127.0.0.1&#39;,
                &#39;port&#39;     =&amp;gt; &#39;6379&#39;,
                &#39;database&#39; =&amp;gt; &#39;0&#39;,
                &#39;password&#39; =&amp;gt; &#39;&#39;,
            ],
        ],
        &#39;page_cache&#39; =&amp;gt; [
            &#39;backend&#39; =&amp;gt; &#39;Magento\\Framework\\Cache\\Backend\\Redis&#39;,
            &#39;backend_options&#39; =&amp;gt; [
                &#39;server&#39;   =&amp;gt; &#39;127.0.0.1&#39;,
                &#39;port&#39;     =&amp;gt; &#39;6379&#39;,
                &#39;database&#39; =&amp;gt; &#39;1&#39;,  // separate database for full page cache
            ],
        ],
    ],
],
&#39;session&#39; =&amp;gt; [
    &#39;save&#39;         =&amp;gt; &#39;redis&#39;,
    &#39;redis&#39; =&amp;gt; [
        &#39;host&#39;     =&amp;gt; &#39;127.0.0.1&#39;,
        &#39;port&#39;     =&amp;gt; &#39;6379&#39;,
        &#39;database&#39; =&amp;gt; &#39;2&#39;,
    ],
],&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Use a separate Redis database number for each type. Mixing the page cache and session data in the same database means a cache flush will wipe active sessions, which logs out all your customers. Learned that one the hard way on a live site.
&lt;/p&gt;

&lt;h3&gt;7. Flat Catalog tables speed up large catalogues&lt;/h3&gt;

&lt;p&gt;
By default Magento stores product and category attributes using the EAV (Entity-Attribute-Value) model. For catalogues with thousands of products and many attributes, this means dozens of JOIN operations per product list query. Flat tables consolidate all attributes into a single table per entity type, which is dramatically faster for reads.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Enable flat catalog in admin: Stores &gt; Configuration &gt; Catalog &gt; Catalog
# Then rebuild the flat tables
php bin/magento indexer:reindex catalog_category_flat
php bin/magento indexer:reindex catalog_product_flat&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The tradeoff is that the flat tables need to be kept in sync with the EAV tables, which adds a small overhead to product saves. For read-heavy stores with large catalogues this is almost always worth it.
&lt;/p&gt;

&lt;h3&gt;8. Optimise your indexers and set them to update on schedule&lt;/h3&gt;

&lt;p&gt;
Magento has multiple indexers that rebuild data structures when products, categories, or prices change. Running them on &quot;Update on Save&quot; means every product edit triggers a full or partial reindex, which blocks the save operation and slows down the admin. On stores with large catalogues this can make product editing painfully slow.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Set all indexers to update on schedule
php bin/magento indexer:set-mode schedule

# Or set a specific indexer
php bin/magento indexer:set-mode schedule catalogrule_rule

# Check current indexer status
php bin/magento indexer:status&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
With schedule mode, reindexing happens on cron runs rather than during save operations. Editors get fast saves, the index stays reasonably fresh, and you avoid timeout issues when making bulk changes.
&lt;/p&gt;

&lt;h3&gt;9. Enable Varnish for full page cache in production&lt;/h3&gt;

&lt;p&gt;
Magento&#39;s built-in full page cache is decent but Varnish is significantly faster for high-traffic stores. Magento generates a Varnish configuration file for you:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;php bin/magento varnish:vcl:generate --export-version=6 &amp;gt; /etc/varnish/default.vcl&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Set the caching application to Varnish in the admin under Stores, Configuration, Advanced, System, Full Page Cache. One thing that catches developers out: after enabling Varnish you need to set the HTTP port to 80 and let Varnish handle requests on that port, with Magento behind it on a different port. Your web server config needs to reflect this or you will get redirect loops.
&lt;/p&gt;

&lt;h3&gt;10. Use Asynchronous and deferred indexing for imports&lt;/h3&gt;

&lt;p&gt;
If you run regular product imports via CSV or API, synchronous indexing after each import will slow things to a crawl. Enable asynchronous reindexing and let the cron handle it:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Enable async grid indexing
php bin/magento config:set dev/grid/async_indexing 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
For bulk imports using the Import API, make sure you call &lt;code&gt;php bin/magento indexer:reindex&lt;/code&gt; after the import completes rather than relying on the save events to trigger reindexing. Batch imports at the end, not during.
&lt;/p&gt;


&lt;h2&gt;Backend and Admin Tips&lt;/h2&gt;

&lt;h3&gt;11. Customise Admin Grids Without Rewriting the Whole Grid&lt;/h3&gt;

&lt;p&gt;
One of the most common admin customisations is adding a column to an existing grid, like the orders grid or the customers grid. The wrong way is to override the entire grid PHP class. The right way is to use a UI Component XML file to extend just what you need.
&lt;/p&gt;

&lt;p&gt;
Create a layout XML file in your module at &lt;code&gt;view/adminhtml/layout/sales_order_grid.xml&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;page xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
      xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:View/Layout/etc/page_configuration.xsd&quot;&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;referenceBlock name=&quot;sales_order_grid&quot;&amp;gt;
            &amp;lt;arguments&amp;gt;
                &amp;lt;argument name=&quot;data&quot; xsi:type=&quot;array&quot;&amp;gt;
                    &amp;lt;item name=&quot;config&quot; xsi:type=&quot;array&quot;&amp;gt;
                        &amp;lt;item name=&quot;update_url&quot; xsi:type=&quot;url&quot; path=&quot;mui/index/render&quot;/&amp;gt;
                    &amp;lt;/item&amp;gt;
                &amp;lt;/argument&amp;gt;
            &amp;lt;/arguments&amp;gt;
        &amp;lt;/referenceBlock&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/page&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Then extend the UI component definition in &lt;code&gt;view/adminhtml/ui_component/sales_order_grid.xml&lt;/code&gt; to add your column. This approach survives Magento upgrades far better than class overrides because you are extending, not replacing.
&lt;/p&gt;

&lt;h3&gt;12. Use Virtual types to avoid unnecessary classes&lt;/h3&gt;

&lt;p&gt;
Virtual types are one of Magento&#39;s most underused features. They let you create a variation of an existing class with different constructor arguments, without writing a new PHP file. This is useful when you need multiple instances of the same class configured differently.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!-- di.xml --&amp;gt;
&amp;lt;virtualType name=&quot;Vendor\Module\Model\CustomLogger&quot;
             type=&quot;Magento\Framework\Logger\Monolog&quot;&amp;gt;
    &amp;lt;arguments&amp;gt;
        &amp;lt;argument name=&quot;name&quot; xsi:type=&quot;string&quot;&amp;gt;customModule&amp;lt;/argument&amp;gt;
        &amp;lt;argument name=&quot;handlers&quot; xsi:type=&quot;array&quot;&amp;gt;
            &amp;lt;item name=&quot;system&quot; xsi:type=&quot;object&quot;&amp;gt;
                Vendor\Module\Logger\Handler\Custom
            &amp;lt;/item&amp;gt;
        &amp;lt;/argument&amp;gt;
    &amp;lt;/arguments&amp;gt;
&amp;lt;/virtualType&amp;gt;

&amp;lt;!-- Use the virtual type as a dependency --&amp;gt;
&amp;lt;type name=&quot;Vendor\Module\Model\SomeService&quot;&amp;gt;
    &amp;lt;arguments&amp;gt;
        &amp;lt;argument name=&quot;logger&quot; xsi:type=&quot;object&quot;&amp;gt;
            Vendor\Module\Model\CustomLogger
        &amp;lt;/argument&amp;gt;
    &amp;lt;/arguments&amp;gt;
&amp;lt;/type&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
This creates a logger instance with custom configuration without writing a new Logger class. Clean, upgrade-safe, and takes five minutes once you understand the pattern.
&lt;/p&gt;

&lt;h3&gt;13. Use Plugins (Interceptors) Instead of Rewrites&lt;/h3&gt;

&lt;p&gt;
Magento 1 developers often reach for class rewrites out of habit. In Magento 2, plugins are almost always the better choice. Plugins let you execute code before, after, or around a public method of any class without replacing the entire class.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!-- di.xml --&amp;gt;
&amp;lt;type name=&quot;Magento\Catalog\Model\Product&quot;&amp;gt;
    &amp;lt;plugin name=&quot;vendor_module_product_plugin&quot;
            type=&quot;Vendor\Module\Plugin\ProductPlugin&quot;
            sortOrder=&quot;10&quot;
            disabled=&quot;false&quot;/&amp;gt;
&amp;lt;/type&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Vendor\Module\Plugin;

use Magento\Catalog\Model\Product;

class ProductPlugin
{
    // Runs after getName() and can modify the return value
    public function afterGetName(Product $subject, string $result): string
    {
        if ($subject-&amp;gt;getTypeId() === &#39;bundle&#39;) {
            return $result . &#39; (Bundle)&#39;;
        }

        return $result;
    }

    // Runs before setPrice() and can modify the arguments
    public function beforeSetPrice(Product $subject, float $price): array
    {
        // Ensure price is never negative
        return [max(0, $price)];
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Multiple plugins can target the same method and the &lt;code&gt;sortOrder&lt;/code&gt; attribute controls execution order. This means your customisation coexists with other modules&#39; plugins rather than one rewrite blocking another entirely.
&lt;/p&gt;

&lt;h3&gt;14. Log to custom files, not the default system log&lt;/h3&gt;

&lt;p&gt;
Everything in Magento logs to &lt;code&gt;var/log/system.log&lt;/code&gt; by default. On an active store this file grows fast and becomes almost useless for finding specific module issues. Create a custom logger for your module that writes to its own file:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Vendor\Module\Logger;

use Monolog\Logger;

class Handler extends \Magento\Framework\Logger\Handler\Base
{
    protected $loggerType = Logger::DEBUG;
    protected $fileName   = &#39;/var/log/vendor_module.log&#39;;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Register this handler in your module&#39;s &lt;code&gt;di.xml&lt;/code&gt; using a virtual type (see tip 12 above) and inject it wherever you need logging. Your module&#39;s debug output goes to its own file and does not get buried in the system log noise.
&lt;/p&gt;


&lt;h2&gt;Frontend and Theming Tips&lt;/h2&gt;

&lt;h3&gt;15. Extend Parent Themes Rather Than Modifying Them Directly&lt;/h3&gt;

&lt;p&gt;
Never modify Luma or Blank theme files directly. Magento updates will overwrite your changes and you will have no way of knowing what was changed. Always create a child theme that declares the parent:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;!-- app/design/frontend/Vendor/mytheme/theme.xml --&amp;gt;
&amp;lt;theme xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:Config/etc/theme.xsd&quot;&amp;gt;
    &amp;lt;title&amp;gt;Vendor My Theme&amp;lt;/title&amp;gt;
    &amp;lt;parent&amp;gt;Magento/luma&amp;lt;/parent&amp;gt;
&amp;lt;/theme&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
To override a template, copy it from the parent theme into the same relative path in your theme and edit the copy. Magento&#39;s theme fallback system will use your version. To override a layout, create a layout XML file with the same name in your theme&#39;s layout directory and add only the changes you need.
&lt;/p&gt;

&lt;h3&gt;16. Use the less variable override system properly&lt;/h3&gt;

&lt;p&gt;
Magento 2 uses Less for stylesheets and has a variable system that lets you customise colours, fonts, and spacing without touching the source Less files. Override variables in your theme by creating &lt;code&gt;web/css/source/_variables.less&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Override the primary colour across the entire theme
@color-orange: #e84040;

// Change the default font
@font-family__base: &#39;Inter&#39;, sans-serif;
@font-family__serif: Georgia, serif;

// Button styling
@button__background: @color-orange;
@button__border-radius: 4px;

// Navigation
@navigation__background: #1a1a2e;
@navigation-level0-item__color: #ffffff;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
This propagates through Magento&#39;s entire Less stack without you touching a single source file. Most visual customisations can be done entirely through variable overrides. Only write custom Less for things the variable system does not cover.
&lt;/p&gt;

&lt;h3&gt;17. Understand static content deployment&lt;/h3&gt;

&lt;p&gt;
The most common frontend issue I see in production deployments is forgetting to deploy static content, or deploying it for the wrong locales.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Deploy for all themes and locales
php bin/magento setup:static-content:deploy

# Deploy for specific locales only (faster)
php bin/magento setup:static-content:deploy en_US en_GB

# Deploy a specific theme
php bin/magento setup:static-content:deploy -t Vendor/mytheme en_US

# Use parallel deployment for speed on large stores
php bin/magento setup:static-content:deploy --jobs=4 en_US&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
If your static content looks correct in developer mode but breaks in production, you almost certainly have a deployment issue. Check that all your locales are included in the deploy command and that the deployment ran successfully after the last code change.
&lt;/p&gt;

&lt;h3&gt;18. Use RequireJS config to load JavaScript properly&lt;/h3&gt;

&lt;p&gt;
Adding JavaScript in Magento 2 is not as simple as dropping a script tag in a template. The right way is through RequireJS, which manages dependencies and load order. Define your JavaScript modules in &lt;code&gt;view/frontend/requirejs-config.js&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var config = {
    map: {
        &#39;*&#39;: {
            &#39;customSlider&#39;: &#39;Vendor_Module/js/custom-slider&#39;,
        }
    },
    shim: {
        &#39;customSlider&#39;: {
            deps: [&#39;jquery&#39;],
        }
    },
    paths: {
        &#39;slick&#39;: &#39;Vendor_Module/js/vendor/slick.min&#39;,
    }
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Then use it in a template or another JavaScript file:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require([&#39;customSlider&#39;, &#39;jquery&#39;], function(CustomSlider, $) {
    var slider = new CustomSlider({
        element: $(&#39;.product-gallery&#39;),
        autoplay: true,
    });
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The RequireJS approach means your JavaScript is only loaded when needed, dependencies are guaranteed to be available, and your code does not conflict with other modules&#39; JavaScript.
&lt;/p&gt;


&lt;h2&gt;Common Mistakes to avoid&lt;/h2&gt;

&lt;h3&gt;19. Do Not use ObjectManager directly in your code&lt;/h3&gt;

&lt;p&gt;
You will see ObjectManager used in old tutorials and even in some core Magento code. Do not follow that pattern in your own modules. Using ObjectManager directly bypasses dependency injection, makes code untestable, and creates hidden dependencies.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

// Wrong. Do not do this.
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager-&amp;gt;create(\Magento\Catalog\Model\Product::class);

// Right. Inject dependencies through the constructor.
class MyService
{
    public function __construct(
        private \Magento\Catalog\Model\ProductFactory $productFactory
    ) {}

    public function doSomething(): void
    {
        $product = $this-&amp;gt;productFactory-&amp;gt;create();
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The only acceptable place to use ObjectManager directly is in factory classes and proxy classes, and even there Magento generates those automatically through its code generation system.
&lt;/p&gt;

&lt;h3&gt;20. Do Not modify core files&lt;/h3&gt;

&lt;p&gt;
Every Magento update will overwrite your changes. Use plugins, preferences, layout overrides, and template overrides instead. If you find yourself editing a file in &lt;code&gt;vendor/magento/&lt;/code&gt;, stop and find the proper extension point instead. There is always one.
&lt;/p&gt;

&lt;h3&gt;21. Run Code egneration after adding new classes&lt;/h3&gt;

&lt;p&gt;
Magento generates proxy classes, factory classes, and interceptors automatically. When you add a new class that uses dependency injection, you may need to regenerate this code:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Generate interceptors and factories
php bin/magento setup:di:compile

# Clear generated code if something seems wrong
rm -rf generated/code/*
php bin/magento setup:di:compile&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
If you are getting &quot;Class does not exist&quot; errors for factory or proxy classes, this is almost always the fix. In developer mode Magento generates these on the fly, but in production mode they must be compiled ahead of time.
&lt;/p&gt;

&lt;h3&gt;22. Check your module&#39;s sequence in module.xml&lt;/h3&gt;

&lt;p&gt;
If your module depends on another module being loaded first, declare that dependency in &lt;code&gt;etc/module.xml&lt;/code&gt;. Without it, your di.xml or layout XML might be processed before the module it depends on, causing hard-to-debug errors.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;config xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
        xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:Module/etc/module.xsd&quot;&amp;gt;
    &amp;lt;module name=&quot;Vendor_Module&quot; setup_version=&quot;1.0.0&quot;&amp;gt;
        &amp;lt;sequence&amp;gt;
            &amp;lt;module name=&quot;Magento_Catalog&quot;/&amp;gt;
            &amp;lt;module name=&quot;Magento_Sales&quot;/&amp;gt;
        &amp;lt;/sequence&amp;gt;
    &amp;lt;/module&amp;gt;
&amp;lt;/config&amp;gt;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;One last thing worth mentioning&lt;/h2&gt;

&lt;p&gt;
Magento 2 rewards developers who take the time to understand its architecture rather than fighting against it. The dependency injection system, the plugin architecture, the UI component framework, and the layout XML system all have learning curves but they exist for good reasons. Once you work with them properly instead of around them, your code is more maintainable, survives upgrades better, and plays well with other modules.
&lt;/p&gt;

&lt;p&gt;
The most expensive Magento projects I have seen are the ones where developers skipped the architecture and went straight to hacking. The technical debt accumulates fast and the upgrade path becomes a rewrite. The extra time spent doing things the Magento way upfront is always worth it.
&lt;/p&gt;

&lt;p&gt;
If there is a specific Magento 2 topic you want covered in more depth, drop a comment below.
&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/04/magento-2-tips-tricks-developers.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-1622739233428768031</guid><pubDate>Sat, 04 Apr 2026 17:01:00 +0000</pubDate><atom:updated>2026-04-04T10:01:31.265-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><title>Laravel and Prism PHP: The Modern Way to Work with AI Models</title><description>&lt;p&gt;
Every Laravel project that needs AI ends up with a different implementation. One project uses the OpenAI PHP client directly. Another one uses a wrapper someone wrote three years ago that is no longer maintained. A third one is tightly coupled to a specific model, so switching from GPT-4o to Claude requires rewriting half the service layer.
&lt;/p&gt;

&lt;p&gt;
Prism PHP solves this properly. It is a Laravel package that gives you a single, consistent API for working with multiple AI providers. OpenAI, Anthropic Claude, Ollama for local models, Mistral, Gemini, and more, all through the same fluent interface. You switch providers by changing one line. Your application code does not care which model is behind it.
&lt;/p&gt;

&lt;p&gt;
This post covers the full picture. All the supported providers and when to use each one, text generation with structured output, tool calling so your AI can actually interact with your application, and embeddings for semantic search. I will tie all three together with a real-world example at the end so you can see how they work as a system rather than isolated features.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you need:&lt;/strong&gt; &lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Laravel 10 or 11&lt;/li&gt;
  &lt;li&gt;PHP 8.1+&lt;/li&gt;
  &lt;li&gt;Composer&lt;/li&gt;
  &lt;li&gt;API keys for whichever providers you plan to use.&lt;/li&gt;
  &lt;li&gt;Ollama requires a local install but is free to run.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Why Prism instead of the OpenAI Client directly&lt;/h2&gt;

&lt;p&gt;
The openai-php/laravel client is solid and I have used it in several projects on this blog. But it locks you into OpenAI. If you want to try Claude for a specific task, or use a local Ollama model for development to avoid API costs, you are writing separate integration code for each one.
&lt;/p&gt;

&lt;p&gt;
Prism is inspired by the Vercel AI SDK, which solved this same problem in the JavaScript world. The idea is simple: define a unified interface, write drivers for each provider, and let application code stay completely provider-agnostic. The practical benefits are real.
&lt;/p&gt;

&lt;p&gt;
You can use GPT-4o for general generation, Claude for tasks where it performs better (long document analysis, nuanced writing), and a local Ollama model during development so you are not burning API credits on every test run. All through the same application code. That flexibility is genuinely useful once you start building production AI features.
&lt;/p&gt;


&lt;h2&gt;Supported Providers&lt;/h2&gt;

&lt;p&gt;
Prism currently ships with first-party support for these providers. Each has its own strengths and the right choice depends on the task.
&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Provider&lt;/th&gt;&lt;th&gt;Best For&lt;/th&gt;&lt;th&gt;Key Models&lt;/th&gt;&lt;th&gt;Cost&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;OpenAI&lt;/td&gt;&lt;td&gt;General generation, embeddings, function calling&lt;/td&gt;&lt;td&gt;GPT-4o, GPT-4o-mini, text-embedding-3-small&lt;/td&gt;&lt;td&gt;Pay per token&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Anthropic&lt;/td&gt;&lt;td&gt;Long documents, reasoning, nuanced analysis&lt;/td&gt;&lt;td&gt;Claude 3.7 Sonnet, Claude 3.5 Haiku&lt;/td&gt;&lt;td&gt;Pay per token&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ollama&lt;/td&gt;&lt;td&gt;Local development, privacy-sensitive data, zero API cost&lt;/td&gt;&lt;td&gt;Llama 3, Mistral, Phi-3, any Ollama model&lt;/td&gt;&lt;td&gt;Free (runs locally)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mistral&lt;/td&gt;&lt;td&gt;Efficient generation, European data residency&lt;/td&gt;&lt;td&gt;Mistral Large, Mistral Small&lt;/td&gt;&lt;td&gt;Pay per token&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Google Gemini&lt;/td&gt;&lt;td&gt;Multimodal tasks, audio and video input&lt;/td&gt;&lt;td&gt;Gemini 1.5 Flash, Gemini 1.5 Pro&lt;/td&gt;&lt;td&gt;Pay per token&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;xAI (Grok)&lt;/td&gt;&lt;td&gt;Real-time data awareness, alternative to GPT-4&lt;/td&gt;&lt;td&gt;Grok-2&lt;/td&gt;&lt;td&gt;Pay per token&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
My general approach: OpenAI for embeddings and general tasks, Claude for anything involving long content or nuanced judgment, Ollama for local development. That combination covers most application needs while keeping costs reasonable.
&lt;/p&gt;


&lt;h2&gt;Installation and Configuration&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;composer require prism-php/prism&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Publish the config file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;php artisan vendor:publish --tag=prism-config&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
This generates &lt;code&gt;config/prism.php&lt;/code&gt;. Add your provider credentials to &lt;code&gt;.env&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# OpenAI
OPENAI_API_KEY=sk-your-openai-key

# Anthropic
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key

# Mistral
MISTRAL_API_KEY=your-mistral-key

# Google Gemini
GEMINI_API_KEY=your-gemini-key

# xAI
XAI_API_KEY=your-xai-key

# Ollama runs locally, no API key needed
# Default URL is http://localhost:11434&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The &lt;code&gt;config/prism.php&lt;/code&gt; file maps these to the relevant providers. You can also set default models per provider here, which saves repeating the model name in every call.
&lt;/p&gt;


&lt;h2&gt;Part 1: Text Generation and Structured Output&lt;/h2&gt;

&lt;p&gt;
The core feature and the one you will use most. Prism&#39;s text generation API is chainable and reads naturally, which is one of the things that makes it feel like a proper Laravel package rather than a thin wrapper.
&lt;/p&gt;

&lt;h3&gt;Basic Text Generation&lt;/h3&gt;

&lt;p&gt;Here is the same prompt sent to three different providers, with zero application code changes between them:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;

// OpenAI
$response = Prism::text()
    -&amp;gt;using(Provider::OpenAI, &#39;gpt-4o&#39;)
    -&amp;gt;withSystemPrompt(&#39;You are a helpful PHP development assistant.&#39;)
    -&amp;gt;withPrompt(&#39;Explain what a service container is in Laravel.&#39;)
    -&amp;gt;asText();

echo $response-&amp;gt;text;

// Swap to Claude, same code
$response = Prism::text()
    -&amp;gt;using(Provider::Anthropic, &#39;claude-3-7-sonnet-latest&#39;)
    -&amp;gt;withSystemPrompt(&#39;You are a helpful PHP development assistant.&#39;)
    -&amp;gt;withPrompt(&#39;Explain what a service container is in Laravel.&#39;)
    -&amp;gt;asText();

echo $response-&amp;gt;text;

// Or run it locally with Ollama during development
$response = Prism::text()
    -&amp;gt;using(Provider::Ollama, &#39;llama3&#39;)
    -&amp;gt;withSystemPrompt(&#39;You are a helpful PHP development assistant.&#39;)
    -&amp;gt;withPrompt(&#39;Explain what a service container is in Laravel.&#39;)
    -&amp;gt;asText();

echo $response-&amp;gt;text;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
That is the core value proposition right there. Same interface, different provider. If OpenAI has an outage or you want to A/B test Claude versus GPT-4o on a specific prompt, you change one line.
&lt;/p&gt;

&lt;h3&gt;Structured Output with Schema Validation&lt;/h3&gt;

&lt;p&gt;
Getting raw text back is fine for simple tasks. For anything that feeds into application logic, you want structured output. Prism handles this through schema definitions that map directly to PHP objects.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\IntegerSchema;
use Prism\Prism\Schema\ArraySchema;

$schema = new ObjectSchema(
    name: &#39;article_analysis&#39;,
    description: &#39;Analysis of a PHP tutorial article&#39;,
    properties: [
        new StringSchema(&#39;summary&#39;, &#39;One sentence summary of the article&#39;),
        new IntegerSchema(&#39;difficulty_level&#39;, &#39;Difficulty from 1 (beginner) to 5 (expert)&#39;),
        new StringSchema(&#39;primary_topic&#39;, &#39;The main topic of the article&#39;),
        new ArraySchema(
            &#39;key_concepts&#39;,
            &#39;Key technical concepts covered&#39;,
            new StringSchema(&#39;concept&#39;, &#39;A technical concept mentioned in the article&#39;)
        ),
        new ArraySchema(
            &#39;prerequisite_knowledge&#39;,
            &#39;What the reader should know before reading this&#39;,
            new StringSchema(&#39;prerequisite&#39;, &#39;A prerequisite concept or skill&#39;)
        ),
    ],
    requiredFields: [&#39;summary&#39;, &#39;difficulty_level&#39;, &#39;primary_topic&#39;, &#39;key_concepts&#39;]
);

$articleContent = &quot;Your article text goes here...&quot;;

$response = Prism::text()
    -&amp;gt;using(Provider::OpenAI, &#39;gpt-4o&#39;)
    -&amp;gt;withSystemPrompt(&#39;You analyse PHP and Laravel tutorial articles.&#39;)
    -&amp;gt;withPrompt(&quot;Analyse this article:\n\n{$articleContent}&quot;)
    -&amp;gt;withSchema($schema)
    -&amp;gt;asStructured();

// $response-&gt;structured is a fully typed PHP array matching your schema
$analysis = $response-&amp;gt;structured;

echo $analysis[&#39;summary&#39;];
echo $analysis[&#39;difficulty_level&#39;];
echo implode(&#39;, &#39;, $analysis[&#39;key_concepts&#39;]);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
No more parsing freeform text. No more stripping markdown fences from JSON responses. Prism handles the structured output negotiation with the model and gives you a validated PHP array. If the model returns something that does not match the schema, Prism throws rather than silently returning garbage data.
&lt;/p&gt;

&lt;h3&gt;Multi-Turn Conversations&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\ValueObjects\Messages\UserMessage;
use Prism\Prism\ValueObjects\Messages\AssistantMessage;

$history = [
    new UserMessage(&#39;What is the difference between Laravel jobs and events?&#39;),
    new AssistantMessage(&#39;Jobs are queued tasks for deferred work. Events are for broadcasting that something happened in your application...&#39;),
    new UserMessage(&#39;Can you show me a code example of a job?&#39;),
];

$response = Prism::text()
    -&amp;gt;using(Provider::Anthropic, &#39;claude-3-7-sonnet-latest&#39;)
    -&amp;gt;withSystemPrompt(&#39;You are a Laravel expert.&#39;)
    -&amp;gt;withMessages($history)
    -&amp;gt;asText();

echo $response-&amp;gt;text;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Part 2: Tool Calling&lt;/h2&gt;

&lt;p&gt;
Tool calling is where things get genuinely interesting. Instead of the AI just generating text, you give it tools it can call, functions that interact with your actual application. The model decides when to use a tool based on the user&#39;s request, calls it, gets the result, and incorporates it into its response.
&lt;/p&gt;

&lt;p&gt;
Without tool calling, an AI assistant can only work with what it was trained on. With tool calling, it can check live database records, call external APIs, perform calculations, and do anything else you give it a tool for.
&lt;/p&gt;

&lt;h3&gt;Defining a Tool&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Tool;

// A tool that looks up an order from your database
$orderLookupTool = Tool::as(&#39;get_order_status&#39;)
    -&amp;gt;for(&#39;Look up the status and details of a customer order by order number&#39;)
    -&amp;gt;withStringParameter(&#39;order_number&#39;, &#39;The order number to look up, e.g. ORD-2025-1234&#39;)
    -&amp;gt;using(function (string $order_number): string {
        $order = \App\Models\Order::where(&#39;order_number&#39;, $order_number)
            -&amp;gt;with(&#39;items&#39;)
            -&amp;gt;first();

        if (!$order) {
            return json_encode([&#39;error&#39; =&amp;gt; &#39;Order not found&#39;]);
        }

        return json_encode([
            &#39;order_number&#39; =&amp;gt; $order-&amp;gt;order_number,
            &#39;status&#39;       =&amp;gt; $order-&amp;gt;status,
            &#39;placed_at&#39;    =&amp;gt; $order-&amp;gt;created_at-&amp;gt;format(&#39;d M Y&#39;),
            &#39;total&#39;        =&amp;gt; &#39;$&#39; . number_format($order-&amp;gt;total, 2),
            &#39;items&#39;        =&amp;gt; $order-&amp;gt;items-&amp;gt;count(),
            &#39;tracking&#39;     =&amp;gt; $order-&amp;gt;tracking_number ?? &#39;Not yet assigned&#39;,
        ]);
    });&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The model sees the tool name, description, and parameter definitions. When a user asks something like &quot;what is the status of my order ORD-2025-4821&quot;, the model recognises it needs the order lookup tool, calls it with the order number extracted from the message, gets the JSON result back, and uses it to form a natural language response.
&lt;/p&gt;

&lt;h3&gt;Using Multiple Tools Together&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Tool;

$orderLookupTool = Tool::as(&#39;get_order_status&#39;)
    -&amp;gt;for(&#39;Look up order status by order number&#39;)
    -&amp;gt;withStringParameter(&#39;order_number&#39;, &#39;The order number&#39;)
    -&amp;gt;using(function (string $order_number): string {
        // Your order lookup logic here
        return json_encode([&#39;status&#39; =&amp;gt; &#39;shipped&#39;, &#39;tracking&#39; =&amp;gt; &#39;TRK123456&#39;]);
    });

$productSearchTool = Tool::as(&#39;search_products&#39;)
    -&amp;gt;for(&#39;Search the product catalogue by keyword&#39;)
    -&amp;gt;withStringParameter(&#39;keyword&#39;, &#39;Search term to find products&#39;)
    -&amp;gt;withIntegerParameter(&#39;limit&#39;, &#39;Maximum number of results to return&#39;)
    -&amp;gt;using(function (string $keyword, int $limit = 5): string {
        $products = \App\Models\Product::search($keyword)
            -&amp;gt;take($limit)
            -&amp;gt;get([&#39;name&#39;, &#39;price&#39;, &#39;in_stock&#39;]);

        return json_encode($products-&amp;gt;toArray());
    });

$refundPolicyTool = Tool::as(&#39;get_refund_policy&#39;)
    -&amp;gt;for(&#39;Retrieve the current refund and returns policy&#39;)
    -&amp;gt;using(function (): string {
        return &quot;Orders can be returned within 30 days of delivery. &quot; .
               &quot;Refunds process within 3 to 5 business days. &quot; .
               &quot;Items must be unused and in original packaging.&quot;;
    });

$response = Prism::text()
    -&amp;gt;using(Provider::OpenAI, &#39;gpt-4o&#39;)
    -&amp;gt;withSystemPrompt(
        &#39;You are a helpful customer support assistant for an online store. &#39; .
        &#39;Use the available tools to look up order details, products, and policies. &#39; .
        &#39;Always check actual data before making claims about orders or policies.&#39;
    )
    -&amp;gt;withPrompt(&quot;I ordered something last week, order number ORD-2025-4821. &quot; .
                 &quot;Has it shipped yet? Also, what&#39;s your return policy?&quot;)
    -&amp;gt;withTools([$orderLookupTool, $productSearchTool, $refundPolicyTool])
    -&amp;gt;withMaxSteps(5)
    -&amp;gt;asText();

echo $response-&amp;gt;text;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The &lt;code&gt;withMaxSteps(5)&lt;/code&gt; call is important. It limits how many tool calls the model can make in a single request, preventing runaway chains where the model keeps calling tools indefinitely. Five steps is plenty for most support interactions.
&lt;/p&gt;

&lt;p&gt;
What happens behind the scenes: the model reads the user message, decides it needs to call &lt;code&gt;get_order_status&lt;/code&gt; with order number ORD-2025-4821, Prism runs your PHP function, returns the result to the model, the model sees the shipping status, then calls &lt;code&gt;get_refund_policy&lt;/code&gt; for the second question, gets that result, and writes a complete response covering both questions using real data from your application.
&lt;/p&gt;


&lt;h2&gt;Part 3: Embeddings for Semantic Search&lt;/h2&gt;

&lt;p&gt;
Embeddings convert text into a vector, a list of floating point numbers that represent the semantic meaning of the text. Two pieces of text that mean similar things will have vectors that are close together in that high-dimensional space, even if the actual words used are completely different.
&lt;/p&gt;

&lt;p&gt;
Prism handles embeddings through the same clean interface as text generation.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;

// Generate an embedding for a piece of text
$response = Prism::embeddings()
    -&amp;gt;using(Provider::OpenAI, &#39;text-embedding-3-small&#39;)
    -&amp;gt;fromInput(&#39;How do I reset my account password?&#39;)
    -&amp;gt;create();

$vector = $response-&amp;gt;embeddings[0]-&amp;gt;embedding;
// $vector is an array of 1536 floats representing the semantic meaning&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
You can also embed multiple texts in a single API call, which is more efficient when indexing content:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

$response = Prism::embeddings()
    -&amp;gt;using(Provider::OpenAI, &#39;text-embedding-3-small&#39;)
    -&amp;gt;fromInput([
        &#39;How do I reset my password?&#39;,
        &#39;Account recovery steps for locked accounts&#39;,
        &#39;Changing your email address in account settings&#39;,
        &#39;Two-factor authentication setup guide&#39;,
    ])
    -&amp;gt;create();

foreach ($response-&amp;gt;embeddings as $index =&amp;gt; $embedding) {
    echo &quot;Text {$index}: &quot; . count($embedding-&amp;gt;embedding) . &quot; dimensions\n&quot;;
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Real-World Example: Tying All Three Together&lt;/h2&gt;

&lt;p&gt;
Here is where it gets practical. I will build a customer support assistant that uses all three Prism features together: embeddings to find relevant knowledge base articles, tool calling to look up live order data, and structured text generation to produce consistent responses.
&lt;/p&gt;

&lt;p&gt;
The scenario is a support bot for a Laravel e-commerce application. The bot needs to answer questions about orders using real database data, find relevant help articles using semantic search, and produce responses that follow a consistent format.
&lt;/p&gt;

&lt;h3&gt;Database Setup for Knowledge Base&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;php artisan make:migration create_knowledge_base_table&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

Schema::create(&#39;knowledge_base_articles&#39;, function (Blueprint $table) {
    $table-&amp;gt;id();
    $table-&amp;gt;string(&#39;title&#39;);
    $table-&amp;gt;text(&#39;content&#39;);
    $table-&amp;gt;json(&#39;embedding&#39;)-&amp;gt;nullable();
    $table-&amp;gt;string(&#39;category&#39;);
    $table-&amp;gt;timestamps();
});&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The Knowledge Base Indexer&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Services;

use App\Models\KnowledgeBaseArticle;
use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;

class KnowledgeBaseIndexer
{
    public function indexAll(): void
    {
        $articles = KnowledgeBaseArticle::whereNull(&#39;embedding&#39;)-&amp;gt;get();

        foreach ($articles-&amp;gt;chunk(20) as $batch) {
            $texts = $batch-&amp;gt;map(fn($a) =&amp;gt; $a-&amp;gt;title . &quot;\n\n&quot; . $a-&amp;gt;content)
                           -&amp;gt;toArray();

            $response = Prism::embeddings()
                -&amp;gt;using(Provider::OpenAI, &#39;text-embedding-3-small&#39;)
                -&amp;gt;fromInput($texts)
                -&amp;gt;create();

            foreach ($batch as $index =&amp;gt; $article) {
                $article-&amp;gt;update([
                    &#39;embedding&#39; =&amp;gt; $response-&amp;gt;embeddings[$index]-&amp;gt;embedding,
                ]);
            }

            usleep(200000);
        }
    }

    public function findRelevant(string $query, int $topK = 3): array
    {
        $queryResponse = Prism::embeddings()
            -&amp;gt;using(Provider::OpenAI, &#39;text-embedding-3-small&#39;)
            -&amp;gt;fromInput($query)
            -&amp;gt;create();

        $queryVector = $queryResponse-&amp;gt;embeddings[0]-&amp;gt;embedding;

        // Load articles and compute cosine similarity in PHP
        // For production with large knowledge bases, use pgvector instead
        $articles = KnowledgeBaseArticle::whereNotNull(&#39;embedding&#39;)-&amp;gt;get();

        $scored = $articles-&amp;gt;map(function ($article) use ($queryVector) {
            $articleVector = $article-&amp;gt;embedding;
            return [
                &#39;article&#39;    =&amp;gt; $article,
                &#39;similarity&#39; =&amp;gt; $this-&amp;gt;cosineSimilarity($queryVector, $articleVector),
            ];
        })
        -&amp;gt;filter(fn($item) =&amp;gt; $item[&#39;similarity&#39;] &amp;gt; 0.75)
        -&amp;gt;sortByDesc(&#39;similarity&#39;)
        -&amp;gt;take($topK);

        return $scored-&amp;gt;pluck(&#39;article&#39;)-&amp;gt;toArray();
    }

    private function cosineSimilarity(array $a, array $b): float
    {
        $dot  = array_sum(array_map(fn($x, $y) =&amp;gt; $x * $y, $a, $b));
        $magA = sqrt(array_sum(array_map(fn($x) =&amp;gt; $x ** 2, $a)));
        $magB = sqrt(array_sum(array_map(fn($x) =&amp;gt; $x ** 2, $b)));

        return ($magA * $magB) &amp;gt; 0 ? $dot / ($magA * $magB) : 0.0;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The Support Assistant Service&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Services;

use Prism\Prism\Facades\Prism;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Tool;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Prism\Schema\ArraySchema;

class CustomerSupportAssistant
{
    public function __construct(
        private KnowledgeBaseIndexer $knowledgeBase
    ) {}

    public function respond(string $customerMessage, string $customerId): array
    {
        // Step 1: Find relevant knowledge base articles using embeddings
        $relevantArticles = $this-&amp;gt;knowledgeBase-&amp;gt;findRelevant($customerMessage);

        $knowledgeContext = collect($relevantArticles)
            -&amp;gt;map(fn($a) =&amp;gt; &quot;### {$a-&amp;gt;title}\n{$a-&amp;gt;content}&quot;)
            -&amp;gt;join(&quot;\n\n---\n\n&quot;);

        // Step 2: Define tools for live data access
        $orderTool = Tool::as(&#39;get_order&#39;)
            -&amp;gt;for(&#39;Look up order details and status for a specific order number&#39;)
            -&amp;gt;withStringParameter(&#39;order_number&#39;, &#39;The order number, e.g. ORD-2025-1234&#39;)
            -&amp;gt;using(function (string $order_number) use ($customerId): string {
                $order = \App\Models\Order::where(&#39;order_number&#39;, $order_number)
                    -&amp;gt;where(&#39;customer_id&#39;, $customerId)
                    -&amp;gt;with(&#39;items&#39;, &#39;shipment&#39;)
                    -&amp;gt;first();

                if (!$order) {
                    return json_encode([
                        &#39;error&#39; =&amp;gt; &#39;Order not found or does not belong to this customer&#39;,
                    ]);
                }

                return json_encode([
                    &#39;order_number&#39; =&amp;gt; $order-&amp;gt;order_number,
                    &#39;status&#39;       =&amp;gt; $order-&amp;gt;status,
                    &#39;placed_at&#39;    =&amp;gt; $order-&amp;gt;created_at-&amp;gt;format(&#39;d M Y&#39;),
                    &#39;total&#39;        =&amp;gt; &#39;$&#39; . number_format($order-&amp;gt;total, 2),
                    &#39;item_count&#39;   =&amp;gt; $order-&amp;gt;items-&amp;gt;count(),
                    &#39;tracking&#39;     =&amp;gt; $order-&amp;gt;shipment-&amp;gt;tracking_number ?? &#39;Not yet assigned&#39;,
                    &#39;carrier&#39;      =&amp;gt; $order-&amp;gt;shipment-&amp;gt;carrier ?? null,
                    &#39;estimated_delivery&#39; =&amp;gt; $order-&amp;gt;shipment-&amp;gt;estimated_delivery ?? null,
                ]);
            });

        $accountTool = Tool::as(&#39;get_account_info&#39;)
            -&amp;gt;for(&#39;Retrieve customer account information like email and membership status&#39;)
            -&amp;gt;using(function () use ($customerId): string {
                $customer = \App\Models\Customer::find($customerId);

                if (!$customer) {
                    return json_encode([&#39;error&#39; =&amp;gt; &#39;Customer not found&#39;]);
                }

                return json_encode([
                    &#39;name&#39;              =&amp;gt; $customer-&amp;gt;name,
                    &#39;email&#39;             =&amp;gt; $customer-&amp;gt;email,
                    &#39;member_since&#39;      =&amp;gt; $customer-&amp;gt;created_at-&amp;gt;format(&#39;M Y&#39;),
                    &#39;membership_tier&#39;   =&amp;gt; $customer-&amp;gt;tier,
                    &#39;total_orders&#39;      =&amp;gt; $customer-&amp;gt;orders()-&amp;gt;count(),
                ]);
            });

        // Step 3: Define schema for structured response
        $responseSchema = new ObjectSchema(
            name: &#39;support_response&#39;,
            description: &#39;A structured customer support response&#39;,
            properties: [
                new StringSchema(&#39;message&#39;, &#39;The response message to send to the customer&#39;),
                new StringSchema(
                    &#39;escalation_level&#39;,
                    &#39;Whether to escalate: &quot;none&quot;, &quot;agent&quot;, or &quot;manager&quot;&#39;
                ),
                new StringSchema(&#39;sentiment&#39;, &#39;Customer sentiment detected: &quot;positive&quot;, &quot;neutral&quot;, &quot;frustrated&quot;&#39;),
                new ArraySchema(
                    &#39;follow_up_actions&#39;,
                    &#39;Actions the support team should take after this interaction&#39;,
                    new StringSchema(&#39;action&#39;, &#39;A follow-up action item&#39;)
                ),
            ],
            requiredFields: [&#39;message&#39;, &#39;escalation_level&#39;, &#39;sentiment&#39;]
        );

        // Step 4: Generate the response using text generation, tools, and schema
        $systemPrompt = &quot;You are a helpful and empathetic customer support assistant.

Relevant knowledge base articles for this conversation:
{$knowledgeContext}

Guidelines:
- Use the knowledge base content above when it is relevant to the customer&#39;s question.
- Use the available tools to look up live order and account data when needed.
- Keep responses concise and clear, two to three sentences where possible.
- If the customer is frustrated, acknowledge their feelings first before solving the problem.
- Escalate to &#39;agent&#39; if the issue requires human judgment or account changes.
- Escalate to &#39;manager&#39; only if the customer is threatening to leave or requests a manager specifically.
- Never guess at order details, always use the get_order tool for specific order questions.&quot;;

        $response = Prism::text()
            -&amp;gt;using(Provider::Anthropic, &#39;claude-3-7-sonnet-latest&#39;)
            -&amp;gt;withSystemPrompt($systemPrompt)
            -&amp;gt;withPrompt($customerMessage)
            -&amp;gt;withTools([$orderTool, $accountTool])
            -&amp;gt;withMaxSteps(4)
            -&amp;gt;withSchema($responseSchema)
            -&amp;gt;asStructured();

        return $response-&amp;gt;structured;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;The Controller&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\CustomerSupportAssistant;

class SupportController extends Controller
{
    public function __construct(
        private CustomerSupportAssistant $assistant
    ) {}

    public function chat(Request $request)
    {
        $request-&amp;gt;validate([
            &#39;message&#39; =&amp;gt; &#39;required|string|max:1000&#39;,
        ]);

        $customerId = auth()-&amp;gt;id();
        $message    = $request-&amp;gt;input(&#39;message&#39;);

        try {
            $response = $this-&amp;gt;assistant-&amp;gt;respond($message, $customerId);

            return response()-&amp;gt;json([
                &#39;reply&#39;            =&amp;gt; $response[&#39;message&#39;],
                &#39;escalation_level&#39; =&amp;gt; $response[&#39;escalation_level&#39;],
                &#39;sentiment&#39;        =&amp;gt; $response[&#39;sentiment&#39;],
                &#39;follow_up&#39;        =&amp;gt; $response[&#39;follow_up_actions&#39;] ?? [],
            ]);

        } catch (\Exception $e) {
            report($e);
            return response()-&amp;gt;json([
                &#39;reply&#39; =&amp;gt; &#39;Something went wrong. Please try again in a moment.&#39;,
            ], 500);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;What This System Produces&lt;/h3&gt;

&lt;p&gt;
Here is a realistic example of what the full pipeline returns for a frustrated customer asking about a delayed order:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Customer message: &quot;My order ORD-2025-4821 was supposed to arrive three days ago
and I still haven&#39;t received it. This is really frustrating.&quot;

System flow:
1. Embeddings search finds &quot;Shipping delays FAQ&quot; and &quot;How to track your order&quot; articles
2. Claude reads the relevant knowledge base content
3. Claude calls get_order tool with order number ORD-2025-4821
4. Tool returns: { status: &quot;shipped&quot;, tracking: &quot;TRK987654&quot;, carrier: &quot;FedEx&quot;,
                   estimated_delivery: &quot;2 days ago&quot; }
5. Claude generates structured response

Response:
{
  &quot;message&quot;: &quot;I completely understand your frustration, and I am sorry your order
              is running late. I can see ORD-2025-4821 shipped with FedEx and the
              tracking number is TRK987654. FedEx is showing a delay on their end,
              but the package is still in transit. You can track it directly at
              fedex.com using that number for the most current status.&quot;,
  &quot;escalation_level&quot;: &quot;none&quot;,
  &quot;sentiment&quot;: &quot;frustrated&quot;,
  &quot;follow_up_actions&quot;: [
    &quot;Monitor order TRK987654 for delivery confirmation&quot;,
    &quot;If not delivered within 48 hours, initiate trace request with FedEx&quot;,
    &quot;Flag customer account for priority handling on next contact&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The sentiment flag lets your frontend show a different UI for frustrated customers. The escalation level drives routing logic. The follow-up actions can be stored and assigned to your support team automatically. This is not just a chatbot, it is a complete support workflow powered by three Prism features working together.
&lt;/p&gt;


&lt;h2&gt;Testing Prism Code&lt;/h2&gt;

&lt;p&gt;
One of the things that makes Prism genuinely production-ready is its testing utilities. You do not want real API calls firing during unit tests. Prism ships with response faking so you can test your application logic without hitting any external APIs.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Prism\Prism\Facades\Prism;
use Prism\Prism\Testing\PrismFake;
use Prism\Prism\ValueObjects\TextResult;

it(&#39;generates a support response for order queries&#39;, function () {
    $fakeResponse = new TextResult(
        text: &#39;{&quot;message&quot;: &quot;Your order has shipped.&quot;, &quot;escalation_level&quot;: &quot;none&quot;, &quot;sentiment&quot;: &quot;neutral&quot;}&#39;,
        finishReason: &#39;stop&#39;,
        usage: [&#39;prompt_tokens&#39; =&amp;gt; 100, &#39;completion_tokens&#39; =&amp;gt; 50]
    );

    Prism::fake([$fakeResponse]);

    $assistant = app(CustomerSupportAssistant::class);
    $result    = $assistant-&amp;gt;respond(&#39;Where is my order?&#39;, customerId: 1);

    expect($result[&#39;escalation_level&#39;])-&amp;gt;toBe(&#39;none&#39;);
    expect($result[&#39;sentiment&#39;])-&amp;gt;toBe(&#39;neutral&#39;);

    Prism::assertCallCount(1);
    Prism::assertLastCallUsedProvider(&#39;anthropic&#39;);
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
&lt;code&gt;Prism::fake()&lt;/code&gt; intercepts all Prism calls and returns your predefined responses. &lt;code&gt;Prism::assertCallCount()&lt;/code&gt; and &lt;code&gt;Prism::assertLastCallUsedProvider()&lt;/code&gt; let you verify your code is making the right calls. Clean, straightforward, and no real API usage during tests.
&lt;/p&gt;


&lt;h2&gt;Switching Providers Without Touching Application Code&lt;/h2&gt;

&lt;p&gt;
One last thing worth showing explicitly, because it is the whole point of Prism. You can make your provider configurable through your &lt;code&gt;.env&lt;/code&gt; file so you can switch without a code change:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# .env
AI_PROVIDER=anthropic
AI_MODEL=claude-3-7-sonnet-latest&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

// In your service or controller
$response = Prism::text()
    -&amp;gt;using(
        config(&#39;ai.provider&#39;, &#39;openai&#39;),
        config(&#39;ai.model&#39;, &#39;gpt-4o&#39;)
    )
    -&amp;gt;withPrompt($prompt)
    -&amp;gt;asText();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Change the provider in &lt;code&gt;.env&lt;/code&gt;, restart the queue workers, done. No code changes, no redeployment of application logic. That is the practical benefit of a unified interface. You build once against Prism&#39;s API and gain the flexibility to move between providers as your needs evolve, pricing changes, or a new model comes along that performs better on your specific tasks.
&lt;/p&gt;

&lt;p&gt;
Prism is still relatively young but it is actively maintained and the API has stabilised enough to build production features on. For any new Laravel project that involves AI, it is the first package I reach for now. The alternative, direct API clients for each provider, creates the kind of fragmented codebase that becomes a maintenance problem fast.
&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/04/laravel-prism-php-ai-models.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-2561304422137295341</guid><pubDate>Tue, 31 Mar 2026 18:01:00 +0000</pubDate><atom:updated>2026-03-31T11:18:50.624-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Joomla</category><title>Build a RAG Pipeline Inside Joomla for Intelligent Site Search</title><description>&lt;p&gt;
Joomla&#39;s built-in search has always had the same fundamental limitation. It is keyword-based. A visitor types &quot;how do I reset my account&quot; and the search engine looks for articles containing those exact words. If your article uses the phrase &quot;recover your login credentials&quot; instead, it does not show up. The visitor gets no results, concludes your site does not have the answer, and leaves.
&lt;/p&gt;

&lt;p&gt;
This is not a Joomla problem specifically. It is what keyword search does. It matches strings, not meaning. RAG, Retrieval-Augmented Generation, solves this at the architecture level. Instead of matching keywords, it converts both your content and the search query into vector embeddings, finds content that is semantically similar, and uses an LLM to generate a direct answer from that content. A visitor asking &quot;how do I reset my account&quot; gets a proper answer even if none of your articles use those exact words.
&lt;/p&gt;

&lt;p&gt;
I will walk through the full implementation. We will cover the three main vector storage options honestly so you can make the right choice for your setup, then go deep on building the complete RAG pipeline inside a custom Joomla component using PostgreSQL with pgvector and OpenAI.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you need:&lt;/strong&gt; Joomla 4 or 5, PHP 8.1+, Composer, PostgreSQL with the pgvector extension installed, and an OpenAI API key.&lt;/p&gt;


&lt;h2&gt;What RAG Actually Does, Step by Step&lt;/h2&gt;

&lt;p&gt;
Before writing any code it is worth being clear about what the pipeline actually does at each stage. RAG is one of those terms that gets thrown around loosely and the implementation details matter.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Phase 1: Indexing (runs once, then on content updates)
        ↓
Fetch all published Joomla articles
        ↓
Split long articles into chunks
        ↓
Send each chunk to OpenAI Embeddings API
        ↓
Store the chunk text and its embedding vector in PostgreSQL

Phase 2: Search (runs on every user query)
        ↓
User submits a search query
        ↓
Convert query to an embedding vector via OpenAI
        ↓
Find the most semantically similar chunks using pgvector
        ↓
Send retrieved chunks plus the original query to GPT-4o
        ↓
GPT-4o generates a direct answer grounded in your content
        ↓
Return the answer and source article links to the user&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The indexing phase is the slower one and only needs to run when content changes. The search phase is what your visitors experience and it needs to be fast. Keeping those two concerns separate in the architecture makes both easier to manage.
&lt;/p&gt;


&lt;h2&gt;Three Vector Storage Options for Joomla&lt;/h2&gt;

&lt;p&gt;
This is the decision that shapes the rest of the implementation. There is no universally correct answer here, the right choice depends on your infrastructure, team, and content volume.
&lt;/p&gt;

&lt;h3&gt;Option 1: PostgreSQL with pgvector&lt;/h3&gt;

&lt;p&gt;
pgvector is an open source PostgreSQL extension that adds a native vector data type and similarity search operators. You store embeddings directly in a PostgreSQL table alongside your chunk text and metadata. Similarity search runs as a standard SQL query using the cosine distance operator.
&lt;/p&gt;

&lt;p&gt;
The big advantage is that you are not adding a new infrastructure dependency. If you are already running PostgreSQL, this is just an extension install and a new table. Queries are fast, the data lives in your existing database stack, and you have full control. The limitation is that at very large scale, hundreds of thousands of chunks, you need to tune the index carefully to maintain query speed.
&lt;/p&gt;

&lt;p&gt;
This is what we are building in this post. It is the right default for most Joomla sites.
&lt;/p&gt;

&lt;h3&gt;Option 2: MySQL with a Vector Similarity Workaround&lt;/h3&gt;

&lt;p&gt;
Joomla ships with MySQL as its default database, so this is the path of least resistance from an infrastructure standpoint. MySQL 9.0 added experimental vector support but it is not production-ready for most use cases yet. The practical workaround is to store embeddings as JSON or a serialised float array, fetch candidate chunks using a broad text filter, then do the cosine similarity calculation in PHP.
&lt;/p&gt;

&lt;p&gt;
This works for small content sets, a few hundred articles. It gets slow quickly as the content volume grows because you are doing similarity math in PHP rather than in an optimised database index. If your Joomla site runs MySQL and you cannot add PostgreSQL, this is a viable starting point but plan for a migration if the search volume grows.
&lt;/p&gt;

&lt;h3&gt;Option 3: External Vector Store, Pinecone or Qdrant&lt;/h3&gt;

&lt;p&gt;
Pinecone and Qdrant are purpose-built vector databases. You send embeddings to their API, they handle storage and indexing, and you query them via HTTP. Both have generous free tiers for getting started.
&lt;/p&gt;

&lt;p&gt;
The advantage is performance at scale and zero infrastructure management on your end. The disadvantages are an additional external dependency, data leaving your infrastructure, API rate limits, and ongoing costs that grow with your content volume. For enterprise Joomla sites with strict data residency requirements, an external service is often a non-starter.
&lt;/p&gt;

&lt;p&gt;
Good fit for teams that want to move fast without managing PostgreSQL, or sites with very high search volume where a dedicated vector store makes sense operationally.
&lt;/p&gt;

&lt;p&gt;
We are going with pgvector. Here is the full build.
&lt;/p&gt;


&lt;h2&gt;Install pgvector and Set Up the Database Table&lt;/h2&gt;

&lt;p&gt;
First, install the pgvector extension in your PostgreSQL database. If you have superuser access:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE EXTENSION IF NOT EXISTS vector;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
If you are on a managed PostgreSQL service like AWS RDS or Supabase, pgvector is available as an enabled extension in the console without needing superuser access.
&lt;/p&gt;

&lt;p&gt;
Create the table that will store your article chunks and their embeddings. Run this in your PostgreSQL database, this is a separate database from Joomla&#39;s MySQL database:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;CREATE TABLE joomla_article_embeddings (
    id           SERIAL PRIMARY KEY,
    article_id   INTEGER NOT NULL,
    article_title TEXT NOT NULL,
    chunk_index  INTEGER NOT NULL,
    chunk_text   TEXT NOT NULL,
    embedding    vector(1536),
    url          TEXT,
    created_at   TIMESTAMP DEFAULT NOW(),
    updated_at   TIMESTAMP DEFAULT NOW()
);

CREATE INDEX ON joomla_article_embeddings
    USING ivfflat (embedding vector_cosine_ops)
    WITH (lists = 100);

CREATE INDEX ON joomla_article_embeddings (article_id);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The embedding dimension is 1536 because that is what OpenAI&#39;s &lt;code&gt;text-embedding-3-small&lt;/code&gt; model outputs. If you use &lt;code&gt;text-embedding-3-large&lt;/code&gt; instead, change this to 3072. The ivfflat index is what makes similarity search fast at scale. The &lt;code&gt;lists&lt;/code&gt; value of 100 is a reasonable starting point, tune it upward if you have more than 100,000 chunks.
&lt;/p&gt;


&lt;h2&gt;Custom Joomla Component Structure&lt;/h2&gt;

&lt;p&gt;
We will build this as a custom Joomla component. Create the following structure under &lt;code&gt;components/com_ragsearch&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;components/com_ragsearch/
    ragsearch.xml
    src/
        Service/
            OpenAIService.php
            VectorStoreService.php
            ArticleChunkerService.php
            RAGSearchService.php
        Controller/
            SearchController.php
        View/
            Search/
                HtmlView.php
                tmpl/
                    default.php
    tmpl/
        index.php

administrator/components/com_ragsearch/
    src/
        Controller/
            IndexController.php&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Install the OpenAI PHP client and a PostgreSQL driver via Composer in your Joomla root:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;composer require openai-php/client
composer require doctrine/dbal&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;The OpenAI Service&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Service/OpenAIService.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Site\Service;

use OpenAI;

class OpenAIService
{
    private $client;

    public function __construct()
    {
        $params      = \JComponentHelper::getParams(&#39;com_ragsearch&#39;);
        $apiKey      = $params-&amp;gt;get(&#39;openai_api_key&#39;);
        $this-&amp;gt;client = OpenAI::client($apiKey);
    }

    public function embed(string $text): array
    {
        $response = $this-&amp;gt;client-&amp;gt;embeddings()-&amp;gt;create([
            &#39;model&#39; =&amp;gt; &#39;text-embedding-3-small&#39;,
            &#39;input&#39; =&amp;gt; $text,
        ]);

        return $response-&amp;gt;embeddings[0]-&amp;gt;embedding;
    }

    public function embedBatch(array $texts): array
    {
        $response = $this-&amp;gt;client-&amp;gt;embeddings()-&amp;gt;create([
            &#39;model&#39; =&amp;gt; &#39;text-embedding-3-small&#39;,
            &#39;input&#39; =&amp;gt; $texts,
        ]);

        $embeddings = [];
        foreach ($response-&amp;gt;embeddings as $item) {
            $embeddings[$item-&amp;gt;index] = $item-&amp;gt;embedding;
        }

        return $embeddings;
    }

    public function generateAnswer(string $query, array $chunks): string
    {
        $context = implode(&quot;\n\n---\n\n&quot;, array_column($chunks, &#39;chunk_text&#39;));

        $response = $this-&amp;gt;client-&amp;gt;chat()-&amp;gt;create([
            &#39;model&#39;       =&amp;gt; &#39;gpt-4o&#39;,
            &#39;temperature&#39; =&amp;gt; 0.3,
            &#39;max_tokens&#39;  =&amp;gt; 600,
            &#39;messages&#39;    =&amp;gt; [
                [
                    &#39;role&#39;    =&amp;gt; &#39;system&#39;,
                    &#39;content&#39; =&amp;gt; &#39;You are a helpful site assistant. Answer the user question
                                  using only the content provided below. If the content does
                                  not contain enough information to answer, say so honestly.
                                  Do not make up information. Keep answers clear and concise.&#39;,
                ],
                [
                    &#39;role&#39;    =&amp;gt; &#39;user&#39;,
                    &#39;content&#39; =&amp;gt; &quot;Content from our site:\n\n{$context}\n\nQuestion: {$query}&quot;,
                ],
            ],
        ]);

        return $response-&amp;gt;choices[0]-&amp;gt;message-&amp;gt;content;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Notice the &lt;code&gt;embedBatch&lt;/code&gt; method. When indexing articles, sending texts in batches rather than one at a time cuts the number of API calls significantly and speeds up the indexing process. Use it during the indexing phase, use &lt;code&gt;embed&lt;/code&gt; for single query embeddings at search time.
&lt;/p&gt;


&lt;h2&gt;The Article Chunker Service&lt;/h2&gt;

&lt;p&gt;
Long articles need to be split into chunks before embedding. Embedding an entire 3,000-word article as a single vector produces a representation that is too diffuse to be useful for retrieval. Smaller focused chunks give the similarity search something meaningful to match against.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/Service/ArticleChunkerService.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Site\Service;

class ArticleChunkerService
{
    private int $chunkSize    = 400;
    private int $chunkOverlap = 50;

    public function chunk(string $text): array
    {
        // Strip HTML tags from article body
        $clean = strip_tags($text);

        // Normalise whitespace
        $clean = preg_replace(&#39;/\s+/&#39;, &#39; &#39;, $clean);
        $clean = trim($clean);

        $words  = explode(&#39; &#39;, $clean);
        $total  = count($words);
        $chunks = [];
        $start  = 0;

        while ($start &amp;lt; $total) {
            $end        = min($start + $this-&amp;gt;chunkSize, $total);
            $chunkWords = array_slice($words, $start, $end - $start);
            $chunks[]   = implode(&#39; &#39;, $chunkWords);

            // Move forward by chunkSize minus overlap
            // so consecutive chunks share some context
            $start += ($this-&amp;gt;chunkSize - $this-&amp;gt;chunkOverlap);

            if ($start &amp;gt;= $total) {
                break;
            }
        }

        return array_filter($chunks, fn($c) =&amp;gt; strlen(trim($c)) &amp;gt; 50);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The overlap between chunks matters more than it might seem. If a key sentence sits right at the boundary between two chunks, without overlap it gets split in half and neither chunk represents that idea well. A 50-word overlap means boundary content appears in both adjacent chunks, so the similarity search is more likely to retrieve it when it is relevant.
&lt;/p&gt;


&lt;h2&gt;The Vector Store Service&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Service/VectorStoreService.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Site\Service;

use Doctrine\DBAL\DriverManager;

class VectorStoreService
{
    private $conn;

    public function __construct()
    {
        $params      = \JComponentHelper::getParams(&#39;com_ragsearch&#39;);

        $this-&amp;gt;conn = DriverManager::getConnection([
            &#39;dbname&#39;   =&amp;gt; $params-&amp;gt;get(&#39;pg_database&#39;),
            &#39;user&#39;     =&amp;gt; $params-&amp;gt;get(&#39;pg_user&#39;),
            &#39;password&#39; =&amp;gt; $params-&amp;gt;get(&#39;pg_password&#39;),
            &#39;host&#39;     =&amp;gt; $params-&amp;gt;get(&#39;pg_host&#39;, &#39;localhost&#39;),
            &#39;port&#39;     =&amp;gt; $params-&amp;gt;get(&#39;pg_port&#39;, 5432),
            &#39;driver&#39;   =&amp;gt; &#39;pdo_pgsql&#39;,
        ]);
    }

    public function upsertChunk(
        int    $articleId,
        string $title,
        int    $chunkIndex,
        string $chunkText,
        array  $embedding,
        string $url
    ): void {
        // Delete existing chunks for this article and index first
        $this-&amp;gt;conn-&amp;gt;executeStatement(
            &#39;DELETE FROM joomla_article_embeddings
             WHERE article_id = :id AND chunk_index = :idx&#39;,
            [&#39;id&#39; =&amp;gt; $articleId, &#39;idx&#39; =&amp;gt; $chunkIndex]
        );

        $vectorLiteral = &#39;[&#39; . implode(&#39;,&#39;, $embedding) . &#39;]&#39;;

        $this-&amp;gt;conn-&amp;gt;executeStatement(
            &#39;INSERT INTO joomla_article_embeddings
                (article_id, article_title, chunk_index, chunk_text, embedding, url)
             VALUES
                (:article_id, :title, :chunk_index, :chunk_text, :embedding, :url)&#39;,
            [
                &#39;article_id&#39;  =&amp;gt; $articleId,
                &#39;title&#39;       =&amp;gt; $title,
                &#39;chunk_index&#39; =&amp;gt; $chunkIndex,
                &#39;chunk_text&#39;  =&amp;gt; $chunkText,
                &#39;embedding&#39;   =&amp;gt; $vectorLiteral,
                &#39;url&#39;         =&amp;gt; $url,
            ]
        );
    }

    public function similaritySearch(array $queryEmbedding, int $topK = 5): array
    {
        $vectorLiteral = &#39;[&#39; . implode(&#39;,&#39;, $queryEmbedding) . &#39;]&#39;;

        $sql = &quot;SELECT
                    article_id,
                    article_title,
                    chunk_text,
                    url,
                    1 - (embedding &amp;lt;=&amp;gt; :embedding::vector) AS similarity
                FROM joomla_article_embeddings
                ORDER BY embedding &amp;lt;=&amp;gt; :embedding::vector
                LIMIT :limit&quot;;

        $stmt = $this-&amp;gt;conn-&amp;gt;executeQuery(
            $sql,
            [
                &#39;embedding&#39; =&amp;gt; $vectorLiteral,
                &#39;limit&#39;     =&amp;gt; $topK,
            ]
        );

        return $stmt-&amp;gt;fetchAllAssociative();
    }

    public function deleteArticle(int $articleId): void
    {
        $this-&amp;gt;conn-&amp;gt;executeStatement(
            &#39;DELETE FROM joomla_article_embeddings WHERE article_id = :id&#39;,
            [&#39;id&#39; =&amp;gt; $articleId]
        );
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; operator is pgvector&#39;s cosine distance operator. Cosine distance measures the angle between two vectors rather than the straight-line distance between them, which works better for text embeddings because it focuses on direction, meaning, rather than magnitude. The similarity score in the SELECT is calculated as &lt;code&gt;1 - cosine_distance&lt;/code&gt;, so a score of 1.0 is a perfect match and 0.0 is completely unrelated.
&lt;/p&gt;


&lt;h2&gt;The Indexing Controller&lt;/h2&gt;

&lt;p&gt;
This runs from the Joomla administrator backend. It fetches all published articles, chunks them, embeds them in batches, and stores everything in PostgreSQL. You run this once to build the initial index and then on a schedule or via a hook when articles are updated.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;administrator/components/com_ragsearch/src/Controller/IndexController.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Administrator\Controller;

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\Component\Ragsearch\Site\Service\OpenAIService;
use Joomla\Component\Ragsearch\Site\Service\VectorStoreService;
use Joomla\Component\Ragsearch\Site\Service\ArticleChunkerService;

class IndexController extends BaseController
{
    private int $batchSize = 20;

    public function build(): void
    {
        $db      = $this-&amp;gt;app-&amp;gt;getDatabase();
        $chunker = new ArticleChunkerService();
        $openai  = new OpenAIService();
        $store   = new VectorStoreService();

        // Fetch all published Joomla articles
        $query = $db-&amp;gt;getQuery(true)
            -&amp;gt;select([&#39;a.id&#39;, &#39;a.title&#39;, &#39;a.introtext&#39;, &#39;a.fulltext&#39;])
            -&amp;gt;from($db-&amp;gt;quoteName(&#39;#__content&#39;, &#39;a&#39;))
            -&amp;gt;where($db-&amp;gt;quoteName(&#39;a.state&#39;) . &#39; = 1&#39;);

        $articles = $db-&amp;gt;setQuery($query)-&amp;gt;loadObjectList();

        $indexed = 0;
        $errors  = 0;

        foreach ($articles as $article) {
            try {
                $fullContent = $article-&amp;gt;title . &quot;\n\n&quot;
                             . strip_tags($article-&amp;gt;introtext) . &quot;\n\n&quot;
                             . strip_tags($article-&amp;gt;fulltext);

                $chunks = $chunker-&amp;gt;chunk($fullContent);

                if (empty($chunks)) {
                    continue;
                }

                $url = \JRoute::_(
                    &#39;index.php?option=com_content&amp;amp;view=article&amp;amp;id=&#39; . $article-&amp;gt;id
                );

                // Delete old embeddings for this article before re-indexing
                $store-&amp;gt;deleteArticle($article-&amp;gt;id);

                // Process chunks in batches to reduce API calls
                $chunkBatches = array_chunk($chunks, $this-&amp;gt;batchSize);

                foreach ($chunkBatches as $batchIndex =&amp;gt; $batch) {
                    $embeddings = $openai-&amp;gt;embedBatch($batch);

                    foreach ($batch as $i =&amp;gt; $chunkText) {
                        $globalIndex = ($batchIndex * $this-&amp;gt;batchSize) + $i;
                        $embedding   = $embeddings[$i] ?? null;

                        if (!$embedding) {
                            continue;
                        }

                        $store-&amp;gt;upsertChunk(
                            $article-&amp;gt;id,
                            $article-&amp;gt;title,
                            $globalIndex,
                            $chunkText,
                            $embedding,
                            $url
                        );
                    }

                    // Small pause between batches to stay within API rate limits
                    usleep(200000);
                }

                $indexed++;

            } catch (\Exception $e) {
                $errors++;
                \JLog::add(
                    &#39;RAG indexing failed for article &#39; . $article-&amp;gt;id . &#39;: &#39; . $e-&amp;gt;getMessage(),
                    \JLog::ERROR,
                    &#39;com_ragsearch&#39;
                );
            }
        }

        $this-&amp;gt;app-&amp;gt;enqueueMessage(
            &quot;Indexing complete. Articles indexed: {$indexed}. Errors: {$errors}.&quot;,
            $errors &amp;gt; 0 ? &#39;warning&#39; : &#39;success&#39;
        );

        $this-&amp;gt;setRedirect(&#39;index.php?option=com_ragsearch&#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;The RAG Search Service&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Service/RAGSearchService.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Site\Service;

class RAGSearchService
{
    public function __construct(
        private OpenAIService     $openai,
        private VectorStoreService $store
    ) {}

    public function search(string $query): array
    {
        if (strlen(trim($query)) &amp;lt; 3) {
            return [
                &#39;answer&#39;  =&amp;gt; &#39;Please enter a more specific question.&#39;,
                &#39;sources&#39; =&amp;gt; [],
            ];
        }

        // Convert the query to a vector embedding
        $queryEmbedding = $this-&amp;gt;openai-&amp;gt;embed($query);

        // Find the most semantically similar chunks
        $chunks = $this-&amp;gt;store-&amp;gt;similaritySearch($queryEmbedding, topK: 5);

        if (empty($chunks)) {
            return [
                &#39;answer&#39;  =&amp;gt; &#39;No relevant content found for your query. Try rephrasing your question.&#39;,
                &#39;sources&#39; =&amp;gt; [],
            ];
        }

        // Filter out low-similarity results
        $relevantChunks = array_filter(
            $chunks,
            fn($c) =&amp;gt; ($c[&#39;similarity&#39;] ?? 0) &amp;gt; 0.75
        );

        if (empty($relevantChunks)) {
            return [
                &#39;answer&#39;  =&amp;gt; &#39;I could not find content closely matching your question. Please try different keywords.&#39;,
                &#39;sources&#39; =&amp;gt; [],
            ];
        }

        // Generate a direct answer grounded in the retrieved chunks
        $answer = $this-&amp;gt;openai-&amp;gt;generateAnswer($query, $relevantChunks);

        // Deduplicate sources by article ID
        $sources = [];
        foreach ($relevantChunks as $chunk) {
            $aid = $chunk[&#39;article_id&#39;];
            if (!isset($sources[$aid])) {
                $sources[$aid] = [
                    &#39;title&#39; =&amp;gt; $chunk[&#39;article_title&#39;],
                    &#39;url&#39;   =&amp;gt; $chunk[&#39;url&#39;],
                ];
            }
        }

        return [
            &#39;answer&#39;  =&amp;gt; $answer,
            &#39;sources&#39; =&amp;gt; array_values($sources),
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The similarity threshold of 0.75 is worth paying attention to. Below that score the retrieved chunks are probably not relevant enough to be useful for generating an answer. You can adjust this up or down depending on how your content is structured and how specific the queries on your site tend to be. Start at 0.75 and tune based on real search results.
&lt;/p&gt;


&lt;h2&gt;The Search Controller and View&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Controller/SearchController.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Joomla\Component\Ragsearch\Site\Controller;

use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\Component\Ragsearch\Site\Service\OpenAIService;
use Joomla\Component\Ragsearch\Site\Service\VectorStoreService;
use Joomla\Component\Ragsearch\Site\Service\RAGSearchService;

class SearchController extends BaseController
{
    public function search(): void
    {
        $query = trim($this-&amp;gt;input-&amp;gt;getString(&#39;q&#39;, &#39;&#39;));

        $result = [&#39;answer&#39; =&amp;gt; &#39;&#39;, &#39;sources&#39; =&amp;gt; [], &#39;query&#39; =&amp;gt; $query];

        if (!empty($query)) {
            try {
                $service = new RAGSearchService(
                    new OpenAIService(),
                    new VectorStoreService()
                );

                $result = array_merge($result, $service-&amp;gt;search($query));

            } catch (\Exception $e) {
                $result[&#39;answer&#39;] = &#39;Search is temporarily unavailable. Please try again shortly.&#39;;
                \JLog::add(&#39;RAG search error: &#39; . $e-&amp;gt;getMessage(), \JLog::ERROR, &#39;com_ragsearch&#39;);
            }
        }

        $this-&amp;gt;app-&amp;gt;setUserState(&#39;com_ragsearch.result&#39;, $result);
        $this-&amp;gt;setRedirect(\JRoute::_(&#39;index.php?option=com_ragsearch&amp;amp;view=search&#39;));
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Blade-equivalent Joomla view template at &lt;code&gt;src/View/Search/tmpl/default.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php defined(&#39;_JEXEC&#39;) or die; ?&amp;gt;

&amp;lt;div class=&quot;rag-search&quot;&amp;gt;

    &amp;lt;form method=&quot;POST&quot; action=&quot;&amp;lt;?php echo JRoute::_(&#39;index.php?option=com_ragsearch&amp;task=search.search&#39;); ?&amp;gt;&quot;&amp;gt;
        &amp;lt;?php echo JHtml::_(&#39;form.token&#39;); ?&amp;gt;
        &amp;lt;input type=&quot;text&quot;
               name=&quot;q&quot;
               value=&quot;&amp;lt;?php echo htmlspecialchars($this-&amp;gt;result[&#39;query&#39;] ?? &#39;&#39;); ?&amp;gt;&quot;
               placeholder=&quot;Ask anything about our site...&quot;
               autocomplete=&quot;off&quot;&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Search&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;?php if (!empty($this-&amp;gt;result[&#39;answer&#39;])) : ?&amp;gt;

        &amp;lt;div class=&quot;rag-answer&quot;&amp;gt;
            &amp;lt;h3&amp;gt;Answer&amp;lt;/h3&amp;gt;
            &amp;lt;p&amp;gt;&amp;lt;?php echo nl2br(htmlspecialchars($this-&amp;gt;result[&#39;answer&#39;])); ?&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;?php if (!empty($this-&amp;gt;result[&#39;sources&#39;])) : ?&amp;gt;
            &amp;lt;div class=&quot;rag-sources&quot;&amp;gt;
                &amp;lt;h4&amp;gt;Sources&amp;lt;/h4&amp;gt;
                &amp;lt;ul&amp;gt;
                    &amp;lt;?php foreach ($this-&amp;gt;result[&#39;sources&#39;] as $source) : ?&amp;gt;
                        &amp;lt;li&amp;gt;
                            &amp;lt;a href=&quot;&amp;lt;?php echo htmlspecialchars($source[&#39;url&#39;]); ?&amp;gt;&quot;&amp;gt;
                                &amp;lt;?php echo htmlspecialchars($source[&#39;title&#39;]); ?&amp;gt;
                            &amp;lt;/a&amp;gt;
                        &amp;lt;/li&amp;gt;
                    &amp;lt;?php endforeach; ?&amp;gt;
                &amp;lt;/ul&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;?php endif; ?&amp;gt;

    &amp;lt;?php endif; ?&amp;gt;

&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Keeping the Index fresh&lt;/h2&gt;

&lt;p&gt;
The index goes stale the moment an article is updated and not re-indexed. There are two clean ways to handle this in Joomla.
&lt;/p&gt;

&lt;p&gt;
The first is a Joomla plugin that hooks into &lt;code&gt;onContentAfterSave&lt;/code&gt; and triggers re-indexing for the saved article specifically. This keeps the index fresh in real time but adds latency to every article save operation.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class PlgContentRagsearchIndex extends JPlugin
{
    public function onContentAfterSave(
        string $context,
        object $article,
        bool   $isNew
    ): void {
        if ($context !== &#39;com_content.article&#39;) {
            return;
        }

        if ((int) $article-&amp;gt;state !== 1) {
            return;
        }

        // Dispatch a Joomla queue task instead of indexing synchronously
        // to avoid blocking the article save response
        \Joomla\CMS\Queue\QueueFacade::push(&#39;ragsearch.index_article&#39;, [
            &#39;article_id&#39; =&amp;gt; $article-&amp;gt;id,
        ]);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The second approach is a scheduled CLI task that re-indexes all articles on a schedule, say every hour or every night. For sites where content does not change frequently, a nightly re-index via Joomla&#39;s task scheduler is simpler and puts zero overhead on the save operation.
&lt;/p&gt;

&lt;p&gt;
For most sites the scheduled approach is the right default. Use the plugin approach only if your content changes continuously throughout the day and freshness matters within minutes.
&lt;/p&gt;


&lt;h2&gt;What this looks like for a Real Visitor&lt;/h2&gt;

&lt;p&gt;
Here is a concrete example. Say your Joomla site has articles about software products and a visitor types: &quot;what happens if I cancel my subscription mid-month?&quot;
&lt;/p&gt;

&lt;p&gt;
Your articles probably use phrases like &quot;pro-rata refund policy&quot;, &quot;billing cycle&quot;, &quot;account downgrade&quot;, not the exact words the visitor used. Keyword search returns nothing. The RAG pipeline converts the query to a vector, finds three chunks from your billing and account articles that are semantically close to that question, feeds them to GPT-4o, and returns something like:
&lt;/p&gt;

&lt;p&gt;
&quot;If you cancel mid-month, your account remains active until the end of your current billing period. You will not be charged for the following month. Refunds for unused days are not issued automatically but can be requested within 7 days of cancellation by contacting support.&quot;
&lt;/p&gt;

&lt;p&gt;
Below the answer, the visitor sees links to the two source articles that information came from. They got a direct answer, they can read the full policy if they want to, and they did not have to trawl through search results guessing which article might be relevant.
&lt;/p&gt;


&lt;h2&gt;A Few things to know before Go Live&lt;/h2&gt;

&lt;p&gt;
The embedding cost for the initial indexing run is usually smaller than people expect. A site with 500 articles at 400 words each, split into chunks of 400 words with 50-word overlap, produces roughly 600 to 700 chunks. At OpenAI&#39;s current pricing for &lt;code&gt;text-embedding-3-small&lt;/code&gt;, that initial index costs well under a dollar. Ongoing costs per search query are minimal, one embedding call per query.
&lt;/p&gt;

&lt;p&gt;
Caching search results is worth adding early. Many visitors on the same site ask very similar questions. Store recent query-answer pairs in Joomla&#39;s cache layer with a TTL of a few hours. The cache hit rate on popular queries tends to be high and it cuts both API costs and response time meaningfully.
&lt;/p&gt;

&lt;p&gt;
Finally, keep an eye on what people actually search for. Log the queries, log whether the similarity search returned results above the threshold, and log whether users clicked the source links. After a few weeks you will see which questions the pipeline handles well and which ones consistently miss. That data tells you whether your chunking strategy needs adjusting, whether your similarity threshold is set correctly, and whether there are content gaps on your site worth addressing.
&lt;/p&gt;

&lt;p&gt;
The RAG pattern is one of the most practically useful things you can add to a content-heavy Joomla site. It turns a search box that frustrates visitors into one that actually helps them find what they need, in their own words, without requiring your content to match their exact phrasing.
&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/03/joomla-rag-pipeline-intelligent-site-search.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-7591468006884640831</guid><pubDate>Sat, 28 Mar 2026 13:50:00 +0000</pubDate><atom:updated>2026-03-28T06:50:28.862-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>Drupal and LangChain: Building Multi-Step AI Pipelines for Enterprise CMS</title><description>&lt;p&gt;
Enterprise content teams have a problem that does not get talked about enough. It is not producing content, most large organisations have plenty of that. The problem is what happens to content before it gets published. Review queues that stretch for days, moderation bottlenecks where one editor is the single point of failure, policy checks that get skipped under deadline pressure, and taxonomy tagging that is inconsistent across a team of twenty people all making their own judgment calls.
&lt;/p&gt;

&lt;p&gt;
I worked with an enterprise Drupal site last year that had over 3,000 pieces of content sitting in a moderation queue at any given time. Four editors, no automation, no triage. Good content was getting buried under low-quality submissions and the editors were spending most of their time on mechanical checks rather than actual editorial judgment.
&lt;/p&gt;

&lt;p&gt;
What they needed was a multi-step AI pipeline sitting between content submission and human review. Something that could screen content automatically, flag policy violations, suggest taxonomy terms, score quality, and route content to the right reviewer based on what it found. That is what this post is about.
&lt;/p&gt;

&lt;p&gt;
We will cover how LangChain fits into a Drupal architecture, the honest tradeoffs between the Python, JavaScript, and PHP approaches, and then go deep on building the full pipeline in PHP inside a custom Drupal module.
&lt;/p&gt;


&lt;h2&gt;Why LangChain matters here&lt;/h2&gt;

&lt;p&gt;
LangChain is a framework for building applications that chain multiple AI calls together, each step taking the output of the previous one as its input. Instead of sending one big prompt to an LLM and hoping for a good result, you break the problem into focused steps. One step checks for policy violations. The next scores content quality. The next suggests taxonomy terms. The next decides where to route the content for review. Each step does one thing well.
&lt;/p&gt;

&lt;p&gt;
The reason this matters for an enterprise CMS is that single-prompt AI approaches get inconsistent quickly when content is varied and complex. A one-shot prompt that tries to check policy compliance, assess quality, tag taxonomy, and make a routing decision all at once tends to produce mediocre results across all four. Breaking it into a chain where each step is focused produces meaningfully better output, and more importantly, it makes each step auditable. You can see exactly where the pipeline flagged something and why.
&lt;/p&gt;

&lt;p&gt;
LangChain was originally built in Python, which is where it is most mature. A JavaScript version called LangChain.js followed. There is no official PHP version, which creates an interesting architecture question for Drupal teams.
&lt;/p&gt;


&lt;h2&gt;Three ways to use LangChain with Drupal&lt;/h2&gt;

&lt;p&gt;
Before picking an approach, it is worth understanding what each option actually involves in practice. I have seen teams choose the wrong one based on familiarity rather than fit, and it costs them later.
&lt;/p&gt;

&lt;h3&gt;Option 1: Python LangChain as a Separate Microservice&lt;/h3&gt;

&lt;p&gt;
You build a small Python FastAPI or Flask service that runs LangChain pipelines. Drupal calls this service via HTTP when content needs processing and receives structured JSON back. The pipeline logic lives entirely in Python, Drupal just sends content and handles the response.
&lt;/p&gt;

&lt;p&gt;
This is the most powerful option because you get the full LangChain Python ecosystem, including document loaders, vector stores, agents, and memory. The tradeoff is operational complexity. You are now running and maintaining two separate services, Python and PHP, and your team needs to be comfortable in both.
&lt;/p&gt;

&lt;p&gt;
Good fit for: teams with Python expertise already on staff, complex pipelines that need LangChain agents or vector retrieval, and organisations with proper infrastructure for running multiple services.
&lt;/p&gt;

&lt;h3&gt;Option 2: LangChain.js via a Node.js Microservice&lt;/h3&gt;

&lt;p&gt;
Similar architecture to Option 1 but the sidecar service runs Node.js with LangChain.js instead of Python. Drupal calls it the same way via HTTP. LangChain.js has caught up significantly to the Python version in recent versions and covers most common pipeline patterns.
&lt;/p&gt;

&lt;p&gt;
The advantage over Python is that JavaScript is more widely known across web development teams. The disadvantage is that LangChain.js still lags behind Python on some advanced features, and you still have the same two-service operational overhead.
&lt;/p&gt;

&lt;p&gt;
Good fit for: teams with frontend JavaScript experience who want to avoid Python, simpler pipeline patterns, and organisations already running Node.js services.
&lt;/p&gt;

&lt;h3&gt;Option 3: PHP Pipeline Mimicking LangChain Patterns (What We Are Building)&lt;/h3&gt;

&lt;p&gt;
You implement the same chaining concepts directly in PHP using the OpenAI PHP client, no LangChain library involved. Each step in the pipeline is a focused PHP class. They chain together through a Pipeline orchestrator. The output of each step feeds into the next.
&lt;/p&gt;

&lt;p&gt;
This approach keeps everything inside Drupal, no additional services to deploy or maintain, no cross-language boundaries, no HTTP overhead between steps. The tradeoff is that you implement the chaining logic yourself rather than using a ready-made framework.
&lt;/p&gt;

&lt;p&gt;
Honestly, for most enterprise Drupal use cases this is the right call. The LangChain library provides a lot of features you will not need for a content moderation pipeline. What you need is the chaining pattern, structured prompts, and reliable JSON outputs, and all of that is straightforward to implement in PHP.
&lt;/p&gt;

&lt;p&gt;
Good fit for: Drupal teams without Python or Node.js expertise, pipelines that do not require vector retrieval or complex agents, and organisations that want the full pipeline inside their existing Drupal infrastructure.
&lt;/p&gt;

&lt;p&gt;
That is the option we are going deep on. Here is what we are building.
&lt;/p&gt;


&lt;h2&gt;The Pipeline We Are Building&lt;/h2&gt;

&lt;p&gt;
Four steps, each focused on one job:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Content submitted to Drupal
        ↓
Step 1: Policy Compliance Check
        Does the content violate any publishing policies?
        Output: pass / flag / reject + reason
        ↓
Step 2: Quality Assessment
        Is the content well-written, complete, and suitable for publishing?
        Output: quality score 1-10 + specific feedback
        ↓
Step 3: Taxonomy Suggestion
        What terms should be applied to this content?
        Output: suggested taxonomy terms with confidence scores
        ↓
Step 4: Routing Decision
        Based on the above, where should this content go?
        Output: auto-approve / send to editor / send to senior editor / reject
        ↓
Content routed to correct moderation state in Drupal&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
Each step receives the original content plus the outputs of all previous steps. By the time Step 4 runs, it has the policy check result, the quality score, and the taxonomy suggestions available to inform its routing decision. That context is what makes the routing intelligent rather than mechanical.
&lt;/p&gt;


&lt;h2&gt;Setting up the custom Drupal module&lt;/h2&gt;

&lt;p&gt;
We will build this as a custom Drupal module. Create the module structure:
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;modules/custom/ai_content_pipeline/
    ai_content_pipeline.info.yml
    ai_content_pipeline.services.yml
    ai_content_pipeline.module
    src/
        Pipeline/
            ContentModerationPipeline.php
        Steps/
            PolicyComplianceStep.php
            QualityAssessmentStep.php
            TaxonomySuggestionStep.php
            RoutingDecisionStep.php
        Contracts/
            PipelineStepInterface.php
        Service/
            OpenAIService.php&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;ai_content_pipeline.info.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;name: &#39;AI Content Pipeline&#39;
type: module
description: &#39;Multi-step AI pipeline for intelligent content moderation&#39;
core_version_requirement: ^10 || ^11
package: Custom
dependencies:
  - drupal:node&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Install the OpenAI PHP client via Composer in your Drupal project root:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;composer require openai-php/client&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 1: The Pipeline step interface&lt;/h2&gt;

&lt;p&gt;
Every step in the pipeline implements this interface. It enforces a consistent contract across all steps, which makes the pipeline orchestrator simple to write and easy to extend with new steps later.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/Contracts/PipelineStepInterface.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Contracts;

interface PipelineStepInterface
{
    /**
     * Execute this pipeline step.
     *
     * @param string $content  The original content being processed.
     * @param array  $context  Results from all previous steps.
     *
     * @return array  Results from this step to pass forward.
     */
    public function execute(string $content, array $context): array;

    /**
     * Human-readable name for this step, used in logging.
     */
    public function name(): string;
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 2: The OpenAI Service&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Service/OpenAIService.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Service;

use OpenAI;

class OpenAIService
{
    private $client;

    public function __construct()
    {
        $api_key = \Drupal::config(&#39;ai_content_pipeline.settings&#39;)-&amp;gt;get(&#39;openai_api_key&#39;);
        $this-&amp;gt;client = OpenAI::client($api_key);
    }

    public function chat(string $systemPrompt, string $userMessage): string
    {
        $response = $this-&amp;gt;client-&amp;gt;chat()-&amp;gt;create([
            &#39;model&#39;       =&amp;gt; &#39;gpt-4o&#39;,
            &#39;temperature&#39; =&amp;gt; 0.2,
            &#39;messages&#39;    =&amp;gt; [
                [&#39;role&#39; =&amp;gt; &#39;system&#39;, &#39;content&#39; =&amp;gt; $systemPrompt],
                [&#39;role&#39; =&amp;gt; &#39;user&#39;,   &#39;content&#39; =&amp;gt; $userMessage],
            ],
        ]);

        return $response-&amp;gt;choices[0]-&amp;gt;message-&amp;gt;content;
    }

    public function parseJson(string $raw): array
    {
        $clean = preg_replace(&#39;/^```json\s*/i&#39;, &#39;&#39;, trim($raw));
        $clean = preg_replace(&#39;/```$/&#39;, &#39;&#39;, trim($clean));
        $data  = json_decode(trim($clean), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            return [&#39;error&#39; =&amp;gt; &#39;JSON parse failed&#39;, &#39;raw&#39; =&amp;gt; $raw];
        }

        return $data;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The temperature is set to 0.2, lower than you might expect. For pipeline steps that are making structured decisions, you want as little creative variance as possible. The model should be analytical, not inventive.
&lt;/p&gt;


&lt;h2&gt;Step 3: The Policy Compliance step&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Steps/PolicyComplianceStep.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Steps;

use Drupal\ai_content_pipeline\Contracts\PipelineStepInterface;
use Drupal\ai_content_pipeline\Service\OpenAIService;

class PolicyComplianceStep implements PipelineStepInterface
{
    public function __construct(private OpenAIService $ai) {}

    public function name(): string
    {
        return &#39;Policy Compliance Check&#39;;
    }

    public function execute(string $content, array $context): array
    {
        $system = &#39;You are a content policy compliance reviewer for an enterprise CMS.
                   Review content against publishing policies and return JSON only.
                   No markdown, no explanation outside the JSON object.&#39;;

        $prompt = &amp;lt;&amp;lt;&amp;lt;PROMPT
Review the following content against these publishing policies:

1. No hate speech, discrimination, or offensive language targeting any group.
2. No unverified factual claims presented as established fact.
3. No promotional or advertorial content disguised as editorial.
4. No personally identifiable information about private individuals.
5. No content that could create legal liability (defamation, copyright issues).

Return a JSON object with:
- &quot;status&quot;: one of &quot;pass&quot;, &quot;flag&quot;, or &quot;reject&quot;
- &quot;violations&quot;: array of specific violations found, empty array if none
- &quot;reason&quot;: brief explanation of the status decision

Content to review:
{$content}
PROMPT;

        $raw    = $this-&amp;gt;ai-&amp;gt;chat($system, $prompt);
        $result = $this-&amp;gt;ai-&amp;gt;parseJson($raw);

        return [
            &#39;policy&#39; =&amp;gt; $result,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 4: The Quality Assessment step&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Steps/QualityAssessmentStep.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Steps;

use Drupal\ai_content_pipeline\Contracts\PipelineStepInterface;
use Drupal\ai_content_pipeline\Service\OpenAIService;

class QualityAssessmentStep implements PipelineStepInterface
{
    public function __construct(private OpenAIService $ai) {}

    public function name(): string
    {
        return &#39;Quality Assessment&#39;;
    }

    public function execute(string $content, array $context): array
    {
        $policyStatus = $context[&#39;policy&#39;][&#39;status&#39;] ?? &#39;unknown&#39;;

        $system = &#39;You are a senior editorial quality reviewer for an enterprise CMS.
                   Assess content quality objectively and return JSON only.&#39;;

        $prompt = &amp;lt;&amp;lt;&amp;lt;PROMPT
Assess the quality of the following content. Consider:

- Clarity and readability for a general professional audience
- Completeness: does it cover the topic adequately?
- Structure: is it well organised with a logical flow?
- Accuracy indicators: does it make claims without apparent support?
- Tone: is it appropriate for professional publication?

Note: Policy compliance status from previous check is &quot;{$policyStatus}&quot;.

Return a JSON object with:
- &quot;score&quot;: integer from 1 to 10
- &quot;strengths&quot;: array of what the content does well
- &quot;weaknesses&quot;: array of specific quality issues found
- &quot;publishable&quot;: boolean, true if quality is sufficient for publication

Content:
{$content}
PROMPT;

        $raw    = $this-&amp;gt;ai-&amp;gt;chat($system, $prompt);
        $result = $this-&amp;gt;ai-&amp;gt;parseJson($raw);

        return [
            &#39;quality&#39; =&amp;gt; $result,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 5: The Taxonomy Suggestion step&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/Steps/TaxonomySuggestionStep.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Steps;

use Drupal\ai_content_pipeline\Contracts\PipelineStepInterface;
use Drupal\ai_content_pipeline\Service\OpenAIService;

class TaxonomySuggestionStep implements PipelineStepInterface
{
    private array $availableTerms = [
        &#39;topics&#39;     =&amp;gt; [&#39;Technology&#39;, &#39;Business&#39;, &#39;Health&#39;, &#39;Finance&#39;, &#39;Policy&#39;, &#39;Research&#39;, &#39;Opinion&#39;],
        &#39;audience&#39;   =&amp;gt; [&#39;General&#39;, &#39;Technical&#39;, &#39;Executive&#39;, &#39;Academic&#39;],
        &#39;content_type&#39; =&amp;gt; [&#39;Analysis&#39;, &#39;News&#39;, &#39;Tutorial&#39;, &#39;Case Study&#39;, &#39;Interview&#39;, &#39;Report&#39;],
    ];

    public function __construct(private OpenAIService $ai) {}

    public function name(): string
    {
        return &#39;Taxonomy Suggestion&#39;;
    }

    public function execute(string $content, array $context): array
    {
        $termsJson = json_encode($this-&amp;gt;availableTerms);

        $system = &#39;You are a content taxonomy specialist for an enterprise CMS.
                   Suggest appropriate taxonomy terms and return JSON only.&#39;;

        $prompt = &amp;lt;&amp;lt;&amp;lt;PROMPT
Suggest taxonomy terms for the following content.
Only suggest terms from the available taxonomy list provided.

Available taxonomy terms:
{$termsJson}

Return a JSON object with:
- &quot;suggestions&quot;: object with vocabulary names as keys, each containing:
    - &quot;terms&quot;: array of suggested term names from the available list
    - &quot;confidence&quot;: &quot;high&quot;, &quot;medium&quot;, or &quot;low&quot;
- &quot;primary_topic&quot;: the single most relevant topic term

Content:
{$content}
PROMPT;

        $raw    = $this-&amp;gt;ai-&amp;gt;chat($system, $prompt);
        $result = $this-&amp;gt;ai-&amp;gt;parseJson($raw);

        return [
            &#39;taxonomy&#39; =&amp;gt; $result,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
In a real deployment, replace the hardcoded &lt;code&gt;$availableTerms&lt;/code&gt; array with a dynamic lookup from your Drupal taxonomy vocabularies. You can load terms using Drupal&#39;s entity query system and pass the full list to the prompt.
&lt;/p&gt;


&lt;h2&gt;Step 6: The Routing Decision step&lt;/h2&gt;

&lt;p&gt;
This is where the pipeline pays off. By the time this step runs, it has the policy result, quality score, and taxonomy confidence from the previous three steps. The routing decision is genuinely informed rather than based on a single signal.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/Steps/RoutingDecisionStep.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Steps;

use Drupal\ai_content_pipeline\Contracts\PipelineStepInterface;
use Drupal\ai_content_pipeline\Service\OpenAIService;

class RoutingDecisionStep implements PipelineStepInterface
{
    public function __construct(private OpenAIService $ai) {}

    public function name(): string
    {
        return &#39;Routing Decision&#39;;
    }

    public function execute(string $content, array $context): array
    {
        $policyStatus   = $context[&#39;policy&#39;][&#39;status&#39;]       ?? &#39;unknown&#39;;
        $policyReason   = $context[&#39;policy&#39;][&#39;reason&#39;]       ?? &#39;&#39;;
        $qualityScore   = $context[&#39;quality&#39;][&#39;score&#39;]       ?? 0;
        $publishable    = $context[&#39;quality&#39;][&#39;publishable&#39;]  ?? false;
        $weaknesses     = json_encode($context[&#39;quality&#39;][&#39;weaknesses&#39;] ?? []);
        $violations     = json_encode($context[&#39;policy&#39;][&#39;violations&#39;]  ?? []);

        $system = &#39;You are a content workflow manager for an enterprise CMS.
                   Make routing decisions based on pipeline analysis results.
                   Return JSON only.&#39;;

        $prompt = &amp;lt;&amp;lt;&amp;lt;PROMPT
Based on the pipeline analysis below, decide how this content should be routed.

Pipeline results:
- Policy status: {$policyStatus}
- Policy reason: {$policyReason}
- Policy violations: {$violations}
- Quality score: {$qualityScore} / 10
- Publishable assessment: {$publishable}
- Quality weaknesses: {$weaknesses}

Routing options:
- &quot;auto_approve&quot;: policy passed, quality score 9-10, no issues
- &quot;editor_review&quot;: policy passed, quality score 6-8, minor issues only
- &quot;senior_editor_review&quot;: policy flagged or quality score 4-5, needs experienced judgment
- &quot;reject&quot;: policy status is reject, or quality score below 4

Return a JSON object with:
- &quot;decision&quot;: one of the four routing options above
- &quot;reason&quot;: clear explanation of why this routing was chosen
- &quot;reviewer_notes&quot;: specific things the human reviewer should check, as an array
- &quot;priority&quot;: &quot;high&quot;, &quot;normal&quot;, or &quot;low&quot;
PROMPT;

        $raw    = $this-&amp;gt;ai-&amp;gt;chat($system, $prompt);
        $result = $this-&amp;gt;ai-&amp;gt;parseJson($raw);

        return [
            &#39;routing&#39; =&amp;gt; $result,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 7: The Pipeline Orchestrator&lt;/h2&gt;

&lt;p&gt;
This is the class that wires everything together. It runs each step in sequence, collects the context, handles failures gracefully, and returns the full pipeline result.
&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/Pipeline/ContentModerationPipeline.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace Drupal\ai_content_pipeline\Pipeline;

use Drupal\ai_content_pipeline\Contracts\PipelineStepInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

class ContentModerationPipeline
{
    private array $steps = [];
    private $logger;

    public function __construct(LoggerChannelFactoryInterface $loggerFactory)
    {
        $this-&amp;gt;logger = $loggerFactory-&amp;gt;get(&#39;ai_content_pipeline&#39;);
    }

    public function addStep(PipelineStepInterface $step): self
    {
        $this-&amp;gt;steps[] = $step;
        return $this;
    }

    public function run(string $content): array
    {
        $context   = [];
        $stepLog   = [];
        $startTime = microtime(true);

        foreach ($this-&amp;gt;steps as $step) {
            $stepName  = $step-&amp;gt;name();
            $stepStart = microtime(true);

            try {
                $result  = $step-&amp;gt;execute($content, $context);
                $context = array_merge($context, $result);

                $stepLog[] = [
                    &#39;step&#39;     =&amp;gt; $stepName,
                    &#39;status&#39;   =&amp;gt; &#39;completed&#39;,
                    &#39;duration&#39; =&amp;gt; round(microtime(true) - $stepStart, 2) . &#39;s&#39;,
                ];

                $this-&amp;gt;logger-&amp;gt;info(&#39;Pipeline step completed: @step&#39;, [&#39;@step&#39; =&amp;gt; $stepName]);

            } catch (\Exception $e) {
                $this-&amp;gt;logger-&amp;gt;error(&#39;Pipeline step failed: @step, Error: @error&#39;, [
                    &#39;@step&#39;  =&amp;gt; $stepName,
                    &#39;@error&#39; =&amp;gt; $e-&amp;gt;getMessage(),
                ]);

                $stepLog[] = [
                    &#39;step&#39;   =&amp;gt; $stepName,
                    &#39;status&#39; =&amp;gt; &#39;failed&#39;,
                    &#39;error&#39;  =&amp;gt; $e-&amp;gt;getMessage(),
                ];

                // On failure, route to senior editor for manual review
                $context[&#39;routing&#39;] = [
                    &#39;decision&#39;       =&amp;gt; &#39;senior_editor_review&#39;,
                    &#39;reason&#39;         =&amp;gt; &quot;Pipeline step &#39;{$stepName}&#39; failed. Manual review required.&quot;,
                    &#39;reviewer_notes&#39; =&amp;gt; [&#39;Pipeline encountered an error, please review manually.&#39;],
                    &#39;priority&#39;       =&amp;gt; &#39;high&#39;,
                ];

                break;
            }
        }

        return [
            &#39;context&#39;       =&amp;gt; $context,
            &#39;steps&#39;         =&amp;gt; $stepLog,
            &#39;total_duration&#39; =&amp;gt; round(microtime(true) - $startTime, 2) . &#39;s&#39;,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 8: Wiring it into Drupal&#39;s Moderation workflow&lt;/h2&gt;

&lt;p&gt;
Now we connect the pipeline to Drupal&#39;s content workflow. This hook fires when a node is presaved, runs the pipeline, and applies the routing decision as a moderation state.
&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;ai_content_pipeline.module&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

use Drupal\node\NodeInterface;
use Drupal\ai_content_pipeline\Service\OpenAIService;
use Drupal\ai_content_pipeline\Pipeline\ContentModerationPipeline;
use Drupal\ai_content_pipeline\Steps\PolicyComplianceStep;
use Drupal\ai_content_pipeline\Steps\QualityAssessmentStep;
use Drupal\ai_content_pipeline\Steps\TaxonomySuggestionStep;
use Drupal\ai_content_pipeline\Steps\RoutingDecisionStep;

function ai_content_pipeline_node_presave(NodeInterface $node): void
{
    // Only run on new nodes that are pending moderation
    if (!$node-&amp;gt;isNew()) {
        return;
    }

    $content = $node-&amp;gt;getTitle() . &quot;\n\n&quot; . $node-&amp;gt;get(&#39;body&#39;)-&amp;gt;value;

    if (empty(trim($content))) {
        return;
    }

    $ai       = new OpenAIService();
    $pipeline = new ContentModerationPipeline(\Drupal::service(&#39;logger.factory&#39;));

    $pipeline
        -&amp;gt;addStep(new PolicyComplianceStep($ai))
        -&amp;gt;addStep(new QualityAssessmentStep($ai))
        -&amp;gt;addStep(new TaxonomySuggestionStep($ai))
        -&amp;gt;addStep(new RoutingDecisionStep($ai));

    $result   = $pipeline-&amp;gt;run($content);
    $routing  = $result[&#39;context&#39;][&#39;routing&#39;] ?? null;

    if (!$routing) {
        return;
    }

    // Map routing decision to Drupal moderation states
    $stateMap = [
        &#39;auto_approve&#39;         =&amp;gt; &#39;published&#39;,
        &#39;editor_review&#39;        =&amp;gt; &#39;needs_review&#39;,
        &#39;senior_editor_review&#39; =&amp;gt; &#39;needs_review&#39;,
        &#39;reject&#39;               =&amp;gt; &#39;rejected&#39;,
    ];

    $decision = $routing[&#39;decision&#39;] ?? &#39;editor_review&#39;;
    $state    = $stateMap[$decision] ?? &#39;needs_review&#39;;

    if ($node-&amp;gt;hasField(&#39;moderation_state&#39;)) {
        $node-&amp;gt;set(&#39;moderation_state&#39;, $state);
    }

    // Store pipeline results in a field for reviewer reference
    if ($node-&amp;gt;hasField(&#39;field_ai_review_notes&#39;)) {
        $notes  = &quot;Routing: {$decision}\n&quot;;
        $notes .= &quot;Reason: {$routing[&#39;reason&#39;]}\n\n&quot;;
        $notes .= &quot;Reviewer notes:\n&quot; . implode(&quot;\n&quot;, $routing[&#39;reviewer_notes&#39;] ?? []);
        $node-&amp;gt;set(&#39;field_ai_review_notes&#39;, $notes);
    }
}&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;What the Pipeline output looks like in practice&lt;/h2&gt;

&lt;p&gt;
Here is a realistic example of the full pipeline result for a piece of content that passed policy checks but had quality issues. This is what your editors would see in the review notes field.
&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;context&quot;: {
    &quot;policy&quot;: {
      &quot;status&quot;: &quot;pass&quot;,
      &quot;violations&quot;: [],
      &quot;reason&quot;: &quot;Content meets all publishing policy requirements.&quot;
    },
    &quot;quality&quot;: {
      &quot;score&quot;: 6,
      &quot;publishable&quot;: true,
      &quot;strengths&quot;: [&quot;Clear headline&quot;, &quot;Good factual grounding&quot;],
      &quot;weaknesses&quot;: [&quot;Conclusion is abrupt and underdeveloped&quot;, &quot;Second section lacks supporting evidence&quot;]
    },
    &quot;taxonomy&quot;: {
      &quot;suggestions&quot;: {
        &quot;topics&quot;: { &quot;terms&quot;: [&quot;Technology&quot;, &quot;Business&quot;], &quot;confidence&quot;: &quot;high&quot; },
        &quot;audience&quot;: { &quot;terms&quot;: [&quot;Executive&quot;], &quot;confidence&quot;: &quot;medium&quot; },
        &quot;content_type&quot;: { &quot;terms&quot;: [&quot;Analysis&quot;], &quot;confidence&quot;: &quot;high&quot; }
      },
      &quot;primary_topic&quot;: &quot;Technology&quot;
    },
    &quot;routing&quot;: {
      &quot;decision&quot;: &quot;editor_review&quot;,
      &quot;reason&quot;: &quot;Policy passed but quality score of 6 indicates minor issues that need editorial attention before publication.&quot;,
      &quot;reviewer_notes&quot;: [
        &quot;Strengthen the conclusion, currently ends abruptly&quot;,
        &quot;Add supporting evidence or sources to the second section&quot;,
        &quot;Taxonomy auto-applied, verify the Executive audience tag is correct&quot;
      ],
      &quot;priority&quot;: &quot;normal&quot;
    }
  },
  &quot;steps&quot;: [
    { &quot;step&quot;: &quot;Policy Compliance Check&quot;, &quot;status&quot;: &quot;completed&quot;, &quot;duration&quot;: &quot;1.8s&quot; },
    { &quot;step&quot;: &quot;Quality Assessment&quot;,      &quot;status&quot;: &quot;completed&quot;, &quot;duration&quot;: &quot;2.1s&quot; },
    { &quot;step&quot;: &quot;Taxonomy Suggestion&quot;,     &quot;status&quot;: &quot;completed&quot;, &quot;duration&quot;: &quot;1.6s&quot; },
    { &quot;step&quot;: &quot;Routing Decision&quot;,        &quot;status&quot;: &quot;completed&quot;, &quot;duration&quot;: &quot;1.4s&quot; }
  ],
  &quot;total_duration&quot;: &quot;6.9s&quot;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
The reviewer opens the content, sees it has been routed to them with a quality score of 6, reads the specific reviewer notes, and knows exactly what to look at. No need to read the whole piece from scratch looking for problems. That is the practical value here.
&lt;/p&gt;


&lt;h2&gt;Things worth knowing before you deploy&lt;/h2&gt;

&lt;p&gt;
Seven seconds of pipeline processing on every content submission is not acceptable for a synchronous save operation. Move the pipeline into a queued job that fires after the initial save, using Drupal&#39;s Queue API or a custom queue worker. Store a &quot;pending AI review&quot; state that content sits in while the pipeline runs, then update the moderation state when the job completes.
&lt;/p&gt;

&lt;p&gt;
The system prompts in each step are where the real customisation happens. The policy step above uses generic rules, but for a real enterprise deployment you would replace those with your organisation&#39;s actual editorial policies, pulled from a config form or a dedicated policy content type in Drupal itself. That way non-technical editors can update the policy rules without touching code.
&lt;/p&gt;

&lt;p&gt;
On cost, four GPT-4o calls per content submission adds up across a high-volume site. For content that does not need the full pipeline, like very short pieces or resubmissions, consider a lighter first-pass check using GPT-4o-mini before deciding whether to run the full chain. The classification step costs a fraction of the full pipeline and can filter out a significant portion of submissions early.
&lt;/p&gt;

&lt;p&gt;
Finally, keep the pipeline results. Store each step&#39;s output against the content revision in a custom table or a long text field. After a few months you will have data on what the pipeline flags most often, how accurate the routing decisions are, and where editors are overriding the AI recommendations. That feedback loop is what lets you improve the system prompts over time and actually measure whether the pipeline is helping.
&lt;/p&gt;

&lt;p&gt;
The pattern here, focused steps, structured JSON outputs, full context passed forward, graceful failure handling, is the same pattern LangChain formalises in its framework. Building it directly in PHP means you keep it inside your existing Drupal infrastructure with no additional services to run. For most enterprise Drupal teams, that is the right tradeoff.</description><link>http://www.phpcmsframework.com/2026/03/drupal-langchain-ai-pipeline-enterprise-cms.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total><georss:featurename>HPPQ+43 Monegeetta VIC, Australia</georss:featurename><georss:point>-37.4147339 144.7377328</georss:point><georss:box>-51.578709414860555 127.1596078 -23.250758385139449 162.3158578</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-9061850586803036574</guid><pubDate>Wed, 25 Mar 2026 14:42:00 +0000</pubDate><atom:updated>2026-03-25T07:42:14.257-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><title>Build a WhatsApp AI Assistant Using Laravel, Twilio and OpenAI</title><description>&lt;p&gt;
A few months ago a client came to us with a pretty common problem. Their support team was spending most of the day answering the same twenty questions over and over. Shipping times, return policies, order status, payment methods. The questions were predictable. The answers were documented. But every single one still needed a human to respond.
&lt;/p&gt;
&lt;p&gt;
They were already using WhatsApp for customer communication, so the ask was simple: can we put something intelligent on that channel so the team can focus on the cases that actually need them? That is how we ended up building a WhatsApp AI assistant using Laravel, Twilio, and OpenAI, and it is exactly what this post covers.
&lt;/p&gt;
&lt;p&gt;
By the end you will have a working bot that receives WhatsApp messages through a Twilio webhook, maintains conversation memory per customer so context carries across messages, and uses OpenAI to generate replies that sound like a real support agent. The whole thing runs on standard Laravel, no exotic packages.
&lt;/p&gt;
&lt;h2&gt;What you need:&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Laravel 10 or 11&lt;/li&gt;
    &lt;li&gt;Twilio account with WhatsApp sandbox access&lt;/li&gt;
    &lt;li&gt;OpenAI API key&lt;/li&gt;
    &lt;li&gt;Publicly accessible URL for your webhook&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are working locally, ngrok handles that last part cleanly.&lt;/p&gt;
&lt;h2&gt;How the system works before we write any code&lt;/h2&gt;
&lt;p&gt;
It is worth spending a minute on the architecture before jumping in. When a customer sends a WhatsApp message, Twilio receives it and forwards it to your webhook URL as an HTTP POST request. Laravel handles that request, pulls the customer&#39;s conversation history from cache, appends the new message, sends the full context to OpenAI, gets a reply, stores the updated history back in cache, and sends the response back to Twilio which delivers it to WhatsApp.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Customer sends WhatsApp message
        ↓
Twilio receives it and POSTs to your Laravel webhook
        ↓
Laravel pulls conversation history from Cache
        ↓
Appends new message to history
        ↓
Sends full conversation context to OpenAI
        ↓
OpenAI returns a support reply
        ↓
Laravel stores updated history in Cache
        ↓
Laravel responds with TwiML so Twilio delivers the message
        ↓
Customer receives the reply on WhatsApp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The conversation memory is the part most tutorials skip. Without it, every message the customer sends is treated as a brand new conversation. The bot has no idea what was just discussed. That makes for a frustrating experience, especially in support scenarios where context matters a lot.
&lt;/p&gt;
&lt;h2&gt;Step 1: Install Laravel and required packages&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;composer create-project laravel/laravel whatsapp-ai-assistant
cd whatsapp-ai-assistant
composer require openai-php/laravel twilio/sdk&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Publish the OpenAI config:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan vendor:publish --provider=&quot;OpenAI\Laravel\ServiceProvider&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add your credentials to &lt;code&gt;.env&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OPENAI_API_KEY=sk-your-openai-key-here

TWILIO_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your-auth-token-here
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The number in &lt;code&gt;TWILIO_WHATSAPP_FROM&lt;/code&gt; is Twilio&#39;s shared WhatsApp sandbox number. Once you go to production and get a dedicated number approved by WhatsApp, you update it there.
&lt;/p&gt;
&lt;p&gt;Add the Twilio values to &lt;code&gt;config/services.php&lt;/code&gt; so you can access them cleanly throughout the app:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&#39;twilio&#39; =&amp;gt; [
    &#39;sid&#39;        =&amp;gt; env(&#39;TWILIO_SID&#39;),
    &#39;auth_token&#39; =&amp;gt; env(&#39;TWILIO_AUTH_TOKEN&#39;),
    &#39;from&#39;       =&amp;gt; env(&#39;TWILIO_WHATSAPP_FROM&#39;),
],&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: The Conversation Memory Service&lt;/h2&gt;
&lt;p&gt;
This is the part that makes the bot actually useful in a support context. Each customer gets their own conversation history stored in Laravel Cache, keyed by their WhatsApp number. Every time they send a message, we load their history, add the new message, send the whole thing to OpenAI, then save the updated history back.
&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;app/Services/ConversationMemoryService.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class ConversationMemoryService
{
    private int $maxMessages = 20;
    private int $ttlMinutes  = 60;

    /**
     * Get conversation history for a given WhatsApp number.
     */
    public function getHistory(string $phone): array
    {
        return Cache::get($this-&amp;gt;key($phone), []);
    }

    /**
     * Append a new message to the conversation history.
     */
    public function addMessage(string $phone, string $role, string $content): void
    {
        $history = $this-&amp;gt;getHistory($phone);

        $history[] = [
            &#39;role&#39;    =&amp;gt; $role,
            &#39;content&#39; =&amp;gt; $content,
        ];

        // Keep history trimmed so we do not blow the context window
        if (count($history) &amp;gt; $this-&amp;gt;maxMessages) {
            $history = array_slice($history, -$this-&amp;gt;maxMessages);
        }

        Cache::put($this-&amp;gt;key($phone), $history, now()-&amp;gt;addMinutes($this-&amp;gt;ttlMinutes));
    }

    /**
     * Clear conversation history, useful for reset commands.
     */
    public function clearHistory(string $phone): void
    {
        Cache::forget($this-&amp;gt;key($phone));
    }

    private function key(string $phone): string
    {
        return &#39;whatsapp_conversation_&#39; . md5($phone);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The &lt;code&gt;maxMessages&lt;/code&gt; limit of 20 is deliberate. OpenAI has a context window limit and sending an entire day&#39;s worth of messages in every request gets expensive fast. Keeping the last 20 exchanges gives the bot enough context to be helpful without unnecessary API cost.
&lt;/p&gt;
&lt;p&gt;
The TTL of 60 minutes means if a customer goes quiet for an hour and comes back, the conversation starts fresh. You can adjust both of these to fit your support workflow.
&lt;/p&gt;
&lt;h2&gt;Step 3: The WhatsApp AI Service&lt;/h2&gt;
&lt;p&gt;
This service handles the OpenAI side. It takes the customer&#39;s phone number and their latest message, builds the full conversation context including a system prompt that defines the bot&#39;s behaviour, and returns a reply.
&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;app/Services/WhatsAppAIService.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Services;

use OpenAI\Laravel\Facades\OpenAI;

class WhatsAppAIService
{
    public function __construct(
        private ConversationMemoryService $memory
    ) {}

    public function respond(string $phone, string $userMessage): string
    {
        // Save the customer&#39;s message to history first
        $this-&amp;gt;memory-&amp;gt;addMessage($phone, &#39;user&#39;, $userMessage);

        // Build messages array with system prompt at the top
        $messages = array_merge(
            [$this-&amp;gt;systemPrompt()],
            $this-&amp;gt;memory-&amp;gt;getHistory($phone)
        );

        $response = OpenAI::chat()-&amp;gt;create([
            &#39;model&#39;       =&amp;gt; &#39;gpt-4o&#39;,
            &#39;temperature&#39; =&amp;gt; 0.5,
            &#39;max_tokens&#39;  =&amp;gt; 300,
            &#39;messages&#39;    =&amp;gt; $messages,
        ]);

        $reply = trim($response-&amp;gt;choices[0]-&amp;gt;message-&amp;gt;content);

        // Save the assistant reply to history so context carries forward
        $this-&amp;gt;memory-&amp;gt;addMessage($phone, &#39;assistant&#39;, $reply);

        return $reply;
    }

    private function systemPrompt(): array
    {
        return [
            &#39;role&#39;    =&amp;gt; &#39;system&#39;,
            &#39;content&#39; =&amp;gt; &#39;You are a friendly and professional customer support assistant
                          for an e-commerce store. You help customers with questions about
                          orders, shipping, returns, and payments. Keep replies concise and
                          clear, ideally under 3 sentences, since this is a WhatsApp conversation.
                          If you do not know something specific about an order, ask the customer
                          for their order number and let them know a human agent will follow up.
                          Never make up order details or policies you are not sure about.&#39;,
        ];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
A few things worth pointing out here. The &lt;code&gt;max_tokens: 300&lt;/code&gt; keeps replies short, which is exactly what you want for WhatsApp. Nobody wants to read a five paragraph response on their phone. The system prompt explicitly tells the bot not to make up order details, which is important for a support context where hallucinated information would cause real problems.
&lt;/p&gt;
&lt;p&gt;
The temperature is 0.5, slightly higher than what I used in the code review bot from the last post. Support responses need to feel natural and conversational, so a bit more variation is fine here.
&lt;/p&gt;
&lt;h2&gt;Step 4: The Webhook Controller&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:controller WhatsAppWebhookController&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Services\WhatsAppAIService;
use App\Services\ConversationMemoryService;

class WhatsAppWebhookController extends Controller
{
    public function __construct(
        private WhatsAppAIService $aiService,
        private ConversationMemoryService $memory
    ) {}

    public function handle(Request $request): Response
    {
        $from    = $request-&amp;gt;input(&#39;From&#39;, &#39;&#39;);
        $message = trim($request-&amp;gt;input(&#39;Body&#39;, &#39;&#39;));

        if (empty($from) || empty($message)) {
            return $this-&amp;gt;twiml(&#39;&#39;);
        }

        // Allow customers to reset their conversation
        if (strtolower($message) === &#39;reset&#39;) {
            $this-&amp;gt;memory-&amp;gt;clearHistory($from);
            return $this-&amp;gt;twiml(&#39;Conversation reset. How can I help you today?&#39;);
        }

        // Handle media messages gracefully
        if ($request-&amp;gt;has(&#39;MediaUrl0&#39;)) {
            return $this-&amp;gt;twiml(&#39;Thanks for the image. A human agent will review it and get back to you shortly.&#39;);
        }

        $reply = $this-&amp;gt;aiService-&amp;gt;respond($from, $message);

        return $this-&amp;gt;twiml($reply);
    }

    /**
     * Build a TwiML response that Twilio uses to send the WhatsApp message.
     */
    private function twiml(string $message): Response
    {
        $xml  = &#39;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&#39;;
        $xml .= &#39;&amp;lt;Response&amp;gt;&#39;;
        $xml .= &#39;&amp;lt;Message&amp;gt;&#39; . htmlspecialchars($message) . &#39;&amp;lt;/Message&amp;gt;&#39;;
        $xml .= &#39;&amp;lt;/Response&amp;gt;&#39;;

        return response($xml, 200)-&amp;gt;header(&#39;Content-Type&#39;, &#39;text/xml&#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The reset command is a small touch but worth having. If a customer gets into a confusing exchange and wants to start over, they just send &quot;reset&quot; and the history clears. Useful for testing too.
&lt;/p&gt;
&lt;h2&gt;Step 5: Route and CSRF Exception&lt;/h2&gt;
&lt;p&gt;Add the webhook route in &lt;code&gt;routes/web.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use App\Http\Controllers\WhatsAppWebhookController;

Route::post(&#39;/webhook/whatsapp&#39;, [WhatsAppWebhookController::class, &#39;handle&#39;])
    -&amp;gt;name(&#39;webhook.whatsapp&#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Twilio sends POST requests to your webhook, and Laravel&#39;s CSRF middleware will block them by default because Twilio does not send a CSRF token. You need to exclude this route from CSRF protection.
&lt;/p&gt;
&lt;p&gt;In Laravel 10, open &lt;code&gt;app/Http/Middleware/VerifyCsrfToken.php&lt;/code&gt; and add the route to the exceptions array:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        &#39;webhook/whatsapp&#39;,
    ];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Laravel 11, open &lt;code&gt;bootstrap/app.php&lt;/code&gt; and update it there:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-&amp;gt;withMiddleware(function (Middleware $middleware) {
    $middleware-&amp;gt;validateCsrfTokens(except: [
        &#39;webhook/whatsapp&#39;,
    ]);
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
This is one of those things that trips people up the first time they set up a Twilio webhook on Laravel. The request just silently fails and you get no clear error message. If your webhook is not responding, check this before anything else.
&lt;/p&gt;
&lt;h2&gt;Step 6: Validating That Requests Actually Come From Twilio&lt;/h2&gt;
&lt;p&gt;
Since this webhook is publicly accessible, you should verify that incoming requests actually came from Twilio and not from someone who found your endpoint. Twilio signs every request with your auth token and sends the signature in the &lt;code&gt;X-Twilio-Signature&lt;/code&gt; header.
&lt;/p&gt;
&lt;p&gt;Create a middleware to handle this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:middleware ValidateTwilioRequest&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Twilio\Security\RequestValidator;

class ValidateTwilioRequest
{
    public function handle(Request $request, Closure $next): mixed
    {
        $validator = new RequestValidator(config(&#39;services.twilio.auth_token&#39;));

        $signature = $request-&amp;gt;header(&#39;X-Twilio-Signature&#39;, &#39;&#39;);
        $url       = $request-&amp;gt;fullUrl();
        $params    = $request-&amp;gt;post();

        if (!$validator-&amp;gt;validate($signature, $url, $params)) {
            abort(403, &#39;Invalid Twilio signature.&#39;);
        }

        return $next($request);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply it to the webhook route:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Route::post(&#39;/webhook/whatsapp&#39;, [WhatsAppWebhookController::class, &#39;handle&#39;])
    -&amp;gt;middleware(\App\Http\Middleware\ValidateTwilioRequest::class)
    -&amp;gt;name(&#39;webhook.whatsapp&#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Skip this during local development if it causes issues. Twilio signature validation depends on the exact URL matching, which can get complicated with ngrok. Enable it in staging and production.
&lt;/p&gt;
&lt;h2&gt;Step 7: Exposing Your Local Server With ngrok&lt;/h2&gt;
&lt;p&gt;
Twilio needs a publicly accessible URL to send webhook requests to. During development, ngrok creates a temporary public tunnel to your local machine. Install it from ngrok.com, then run both of these in separate terminals:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan serve&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;ngrok http 8000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
ngrok will give you a forwarding URL that looks something like &lt;code&gt;https://a1b2c3d4.ngrok.io&lt;/code&gt;. Your full webhook URL will be:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://a1b2c3d4.ngrok.io/webhook/whatsapp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Copy that URL, you will need it in the next step.
&lt;/p&gt;
&lt;h2&gt;Step 8: Configure the Twilio WhatsApp Sandbox&lt;/h2&gt;
&lt;p&gt;
Log into your Twilio console and go to Messaging, then Try it out, then Send a WhatsApp message. You will land on the sandbox configuration page.
&lt;/p&gt;
&lt;p&gt;
Find the field labelled &quot;When a message comes in&quot; and paste your ngrok webhook URL there. Make sure the request method is set to HTTP POST, not GET.
&lt;/p&gt;
&lt;p&gt;
To connect your personal WhatsApp number to the sandbox for testing, send the join code shown on that page (something like &quot;join bright-example&quot;) to the Twilio sandbox number on WhatsApp. You will get a confirmation message once you are connected.
&lt;/p&gt;
&lt;p&gt;
Send any message from your WhatsApp now and watch it flow through the full stack.
&lt;/p&gt;
&lt;h2&gt;What a Real Customer Support Conversation Looks Like&lt;/h2&gt;
&lt;p&gt;
Here is an example of the kind of exchange this bot handles well. A customer asking about a return:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Customer : Hi, I want to return a jacket I bought last week

Bot      : Hi there! Happy to help with your return. Could you share your
           order number so I can check the details?

Customer : It is ORDER-48291

Bot      : Thanks! Our standard return window is 30 days from delivery,
           so you are well within that period. Would you like me to walk
           you through the return steps or connect you with a human agent?

Customer : Walk me through it please

Bot      : Sure. Repack the item securely, then visit our returns portal
           and enter your order number to generate a prepaid label. Drop
           it at any courier location and your refund will process within
           3 to 5 business days once we receive it.

Customer : What if I lost the original packaging?

Bot      : No problem at all. Any sturdy box or bag works fine.
           The original packaging is not required for returns.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Four exchanges, the bot carried the order number across messages without the customer repeating it, gave accurate policy information, and offered a clear escalation path. That is exactly what a good first-line support interaction should look like.
&lt;/p&gt;
&lt;h2&gt;Rate Limiting Per Customer&lt;/h2&gt;
&lt;p&gt;
If one customer sends fifty messages in a minute, you do not want to fire fifty OpenAI API calls. Add rate limiting per phone number using Laravel&#39;s built-in rate limiter, right at the top of the handle method in your controller:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use Illuminate\Support\Facades\RateLimiter;

$key = &#39;whatsapp_&#39; . md5($from);

if (RateLimiter::tooManyAttempts($key, 10)) {
    return $this-&amp;gt;twiml(&#39;You are sending messages too quickly. Please wait a moment and try again.&#39;);
}

RateLimiter::hit($key, 60);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
This allows 10 messages per minute per customer before the rate limit kicks in. Adjust the numbers based on how your support volume actually looks.
&lt;/p&gt;
&lt;h2&gt;Moving From Sandbox to Production&lt;/h2&gt;
&lt;p&gt;
The sandbox works well for testing but has real limitations. Every customer has to send a join code before the bot can message them, and the sandbox number is shared across all Twilio accounts. For an actual deployment you need a dedicated WhatsApp Business number approved through Meta.
&lt;/p&gt;
&lt;p&gt;
The approval process goes through Twilio&#39;s WhatsApp sender registration. You submit your business details, Meta reviews and approves the number, and once that is done you update &lt;code&gt;TWILIO_WHATSAPP_FROM&lt;/code&gt; in your production environment and point the webhook to your live URL. The rest of the code does not change.
&lt;/p&gt;
&lt;p&gt;
On the infrastructure side, switch &lt;code&gt;CACHE_DRIVER&lt;/code&gt; to &lt;code&gt;redis&lt;/code&gt; in production. The file cache works locally but Redis handles concurrent requests from multiple customers properly and survives server restarts without losing conversation history mid-session.
&lt;/p&gt;
&lt;h2&gt;Three things to add before handing this to a Client&lt;/h2&gt;
&lt;p&gt;
The core works well but a production support bot needs a bit more to be truly reliable.
&lt;/p&gt;
&lt;p&gt;
First, a database log of every conversation. Both for debugging and for reviewing what the bot is actually saying to customers. A simple &lt;code&gt;whatsapp_messages&lt;/code&gt; table with columns for phone, role, content, and created_at is enough to start. You will thank yourself for having this the first time the bot says something unexpected.
&lt;/p&gt;
&lt;p&gt;
Second, a human handoff trigger. If the customer says something like &quot;I want to speak to a real person&quot; or the bot detects repeated frustration in the conversation, it should stop trying to resolve things automatically and flag the conversation for the support team. A keyword check handles the obvious cases, and you can ask OpenAI to classify sentiment alongside the reply for the subtler ones.
&lt;/p&gt;
&lt;p&gt;
Third, a basic admin view showing active conversations, the most common questions coming in, and average response times. That data is useful for improving the system prompt and for giving the support team visibility into what the bot is handling versus what it is escalating.
&lt;/p&gt;
&lt;p&gt;
Those three additions turn a working prototype into something you can confidently hand over and actually maintain.
&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/03/whatsapp-ai-assistant-laravel-twilio-openai.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-2600599690902117505</guid><pubDate>Sat, 21 Mar 2026 20:35:00 +0000</pubDate><atom:updated>2026-03-21T13:35:56.940-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><title>Build an AI Code Review Bot with Laravel — Real-World Use Case</title><description>&lt;p&gt;
Let me tell you how this idea actually started. A few months back, our team was doing PR reviews and I kept writing the same comment over and over, something like &quot;this will cause an N+1 issue, please use eager loading.&quot; Different developer, different PR, same problem. Third time in two weeks I typed that comment, I thought there has to be a smarter way to handle this first pass.
&lt;/p&gt;
&lt;p&gt;
That is what this is. Not some fancy AI product. Just a practical Laravel tool that takes a PHP code snippet, sends it to OpenAI, and gives back structured feedback before a human reviewer even opens the PR. The idea is simple: catch the obvious stuff automatically so your senior devs can spend their review time on things that actually need a human brain.
&lt;/p&gt;
&lt;p&gt;
I will walk through the full build. By the end you will have a working Laravel app that accepts code, returns severity-tagged issues, security flags, suggestions, and a quality score. We will also hook it up to a queue so the UI does not freeze waiting on the API.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What you need before starting:&lt;/strong&gt; Laravel 10 or 11, PHP 8.1+, Composer, and an OpenAI API key. That is it.&lt;/p&gt;
&lt;h2&gt;Why not PHPStan or CodeSniffer?&lt;/h2&gt;
&lt;p&gt;
Because they are rule-based. They catch what they have been told to catch, nothing more.
&lt;/p&gt;
&lt;p&gt;
PHPStan at max level is genuinely good. I use it. But here is the thing, some of the worst bugs in production do not violate a single linting rule. An N+1 query loop is syntactically perfect. A function that silently returns null on failure will not trigger any warning. A missing authorization check on a route will not show up in static analysis at all.
&lt;/p&gt;
&lt;p&gt;
An LLM understands context. It can look at code and say &quot;this will fall apart under load&quot; or &quot;this validation will silently pass null.&quot; That is a different category of feedback altogether. Use both, they are not competing with each other.
&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;What Gets Checked&lt;/th&gt;&lt;th&gt;PHPStan / PHPCS&lt;/th&gt;&lt;th&gt;AI Reviewer&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Syntax and type errors&lt;/td&gt;&lt;td&gt;Strong&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Coding standards&lt;/td&gt;&lt;td&gt;Strong&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;N+1 / query logic problems&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Security patterns&lt;/td&gt;&lt;td&gt;Partial&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Architecture suggestions&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Explains why something is wrong&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;How everything fits together&lt;/h2&gt;
&lt;p&gt;
Before touching any code, here is the flow:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Developer submits PHP code via a form
        ↓
Laravel controller validates it
        ↓
CodeReviewService builds a structured prompt
        ↓
OpenAI GPT-4o analyses the code
        ↓
JSON response gets parsed
        ↓
Feedback renders back to the developer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
No complex abstractions, no unnecessary packages beyond the OpenAI client. The structure is clean enough that adding features later, storing review history, GitHub webhook triggers, Slack notifications, is straightforward.
&lt;/p&gt;
&lt;h2&gt;Step 1: Install Laravel and the OpenAI Package&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;composer create-project laravel/laravel ai-code-reviewer
cd ai-code-reviewer
composer require openai-php/laravel&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Publish the config file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan vendor:publish --provider=&quot;OpenAI\Laravel\ServiceProvider&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open your &lt;code&gt;.env&lt;/code&gt; and add your key:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OPENAI_API_KEY=sk-your-key-here
OPENAI_ORGANIZATION=&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
One thing I will say plainly. I have seen API keys committed to git repos more times than I would like. Double check that &lt;code&gt;.env&lt;/code&gt; is in your &lt;code&gt;.gitignore&lt;/code&gt; before anything else.
&lt;/p&gt;
&lt;h2&gt;Step 2: Create a Service - CodeReviewService&lt;/h2&gt;
&lt;p&gt;
Third-party API calls belong in a service class. Not in a controller, not in a model. This keeps things testable and means when you want to swap GPT-4o for a different model down the line, you change exactly one file.
&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;app/Services/CodeReviewService.php&lt;/code&gt; manually:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Services;

use OpenAI\Laravel\Facades\OpenAI;

class CodeReviewService
{
    public function review(string $code): array
    {
        $response = OpenAI::chat()-&amp;gt;create([
            &#39;model&#39;       =&amp;gt; &#39;gpt-4o&#39;,
            &#39;temperature&#39; =&amp;gt; 0.3,
            &#39;messages&#39;    =&amp;gt; [
                [
                    &#39;role&#39;    =&amp;gt; &#39;system&#39;,
                    &#39;content&#39; =&amp;gt; &#39;You are a senior PHP developer and Laravel architect.
                                  Review PHP code and return feedback as valid JSON only.
                                  No markdown. No explanation outside the JSON object.&#39;,
                ],
                [
                    &#39;role&#39;    =&amp;gt; &#39;user&#39;,
                    &#39;content&#39; =&amp;gt; $this-&amp;gt;buildPrompt($code),
                ],
            ],
        ]);

        return $this-&amp;gt;parse($response-&amp;gt;choices[0]-&amp;gt;message-&amp;gt;content);
    }

    private function buildPrompt(string $code): string
    {
        return &amp;lt;&amp;lt;&amp;lt;PROMPT
Review the PHP/Laravel code below. Return a JSON object with these keys:

- &quot;summary&quot;: 1-2 sentence overall assessment.
- &quot;score&quot;: integer 1 to 10 for code quality.
- &quot;issues&quot;: array of objects with:
    - &quot;severity&quot;: &quot;critical&quot;, &quot;warning&quot;, or &quot;info&quot;
    - &quot;line_hint&quot;: function name or rough location
    - &quot;message&quot;: clear explanation of the problem
- &quot;suggestions&quot;: array of improvement suggestions as strings.
- &quot;security_flags&quot;: array of security concerns, or empty array.

Code:

\`\`\`php
{$code}
\`\`\`
PROMPT;
    }

    private function parse(string $raw): array
    {
        $clean = preg_replace(&#39;/^```json\s*/i&#39;, &#39;&#39;, trim($raw));
        $clean = preg_replace(&#39;/```$/&#39;, &#39;&#39;, trim($clean));

        $data = json_decode(trim($clean), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            return [
                &#39;summary&#39;        =&amp;gt; &#39;Response could not be parsed. Try submitting again.&#39;,
                &#39;score&#39;          =&amp;gt; null,
                &#39;issues&#39;         =&amp;gt; [],
                &#39;suggestions&#39;    =&amp;gt; [],
                &#39;security_flags&#39; =&amp;gt; [],
            ];
        }

        return $data;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
The &lt;code&gt;temperature: 0.3&lt;/code&gt; is intentional. Lower temperature means less randomness, the model stays focused and gives consistent output. For creative writing you would push that higher. For structured technical analysis, you want predictable not creative.
&lt;/p&gt;
&lt;p&gt;
Also notice the parse method strips markdown fences. GPT-4o usually returns clean JSON when you ask for it, but it occasionally wraps the output in backtick fences anyway. This handles that without breaking anything.
&lt;/p&gt;
&lt;h2&gt;Step 3: Controller and Routes&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:controller CodeReviewController&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\CodeReviewService;

class CodeReviewController extends Controller
{
    public function __construct(
        private CodeReviewService $reviewService
    ) {}

    public function index()
    {
        return view(&#39;code-review.index&#39;);
    }

    public function review(Request $request)
    {
        $request-&amp;gt;validate([
            &#39;code&#39; =&amp;gt; &#39;required|string|min:10|max:5000&#39;,
        ]);

        $feedback = $this-&amp;gt;reviewService-&amp;gt;review($request-&amp;gt;input(&#39;code&#39;));

        return view(&#39;code-review.result&#39;, compact(&#39;feedback&#39;));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the routes in &lt;code&gt;routes/web.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use App\Http\Controllers\CodeReviewController;

Route::get(&#39;/code-review&#39;, [CodeReviewController::class, &#39;index&#39;])
    -&amp;gt;name(&#39;code-review.index&#39;);

Route::post(&#39;/code-review&#39;, [CodeReviewController::class, &#39;review&#39;])
    -&amp;gt;name(&#39;code-review.review&#39;);&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 4: Blade Views&lt;/h2&gt;
&lt;p&gt;
Keeping these minimal. The styling comes from your existing setup, no need to add anything extra here.
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;resources/views/code-review/index.blade.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;AI Code Reviewer&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;h1&amp;gt;AI Code Reviewer&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Paste PHP or Laravel code below and get structured feedback instantly.&amp;lt;/p&amp;gt;

&amp;lt;form method=&quot;POST&quot; action=&quot;{{ route(&#39;code-review.review&#39;) }}&quot;&amp;gt;
    @csrf
    &amp;lt;textarea name=&quot;code&quot; rows=&quot;15&quot; cols=&quot;80&quot;
              placeholder=&quot;Paste your PHP code here...&quot;&amp;gt;{{ old(&#39;code&#39;) }}&amp;lt;/textarea&amp;gt;

    @error(&#39;code&#39;)
        &amp;lt;p&amp;gt;{{ $message }}&amp;lt;/p&amp;gt;
    @enderror

    &amp;lt;br&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;Review Code&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;resources/views/code-review/result.blade.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Review Result&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;h1&amp;gt;Code Review Result&amp;lt;/h1&amp;gt;

&amp;lt;p&amp;gt;{{ $feedback[&#39;summary&#39;] ?? &#39;&#39; }}&amp;lt;/p&amp;gt;

@isset($feedback[&#39;score&#39;])
    &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Quality Score: {{ $feedback[&#39;score&#39;] }} / 10&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
@endisset

@if(!empty($feedback[&#39;issues&#39;]))
    &amp;lt;h2&amp;gt;Issues Found&amp;lt;/h2&amp;gt;
    @foreach($feedback[&#39;issues&#39;] as $issue)
        &amp;lt;div&amp;gt;
            &amp;lt;strong&amp;gt;[{{ strtoupper($issue[&#39;severity&#39;]) }}]&amp;lt;/strong&amp;gt;
            @if(!empty($issue[&#39;line_hint&#39;]))
                , {{ $issue[&#39;line_hint&#39;] }}
            @endif
            &amp;lt;p&amp;gt;{{ $issue[&#39;message&#39;] }}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;hr&amp;gt;
    @endforeach
@else
    &amp;lt;p&amp;gt;No major issues found.&amp;lt;/p&amp;gt;
@endif

@if(!empty($feedback[&#39;security_flags&#39;]))
    &amp;lt;h2&amp;gt;Security Flags&amp;lt;/h2&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach($feedback[&#39;security_flags&#39;] as $flag)
            &amp;lt;li&amp;gt;{{ $flag }}&amp;lt;/li&amp;gt;
        @endforeach
    &amp;lt;/ul&amp;gt;
@endif

@if(!empty($feedback[&#39;suggestions&#39;]))
    &amp;lt;h2&amp;gt;Suggestions&amp;lt;/h2&amp;gt;
    &amp;lt;ul&amp;gt;
        @foreach($feedback[&#39;suggestions&#39;] as $s)
            &amp;lt;li&amp;gt;{{ $s }}&amp;lt;/li&amp;gt;
        @endforeach
    &amp;lt;/ul&amp;gt;
@endif

&amp;lt;p&amp;gt;&amp;lt;a href=&quot;{{ route(&#39;code-review.index&#39;) }}&quot;&amp;gt;Review another snippet&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;Step 5: Queue the API Call, Do not block the UI&lt;/h2&gt;
&lt;p&gt;
GPT-4o usually responds in 2 to 4 seconds for short snippets, sometimes longer. That is not great for a synchronous web request, and on some server configs it will hit a timeout before the response comes back. For any production setup, queue it.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:job ProcessCodeReview&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;?php

namespace App\Jobs;

use App\Services\CodeReviewService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;

class ProcessCodeReview implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 60;
    public int $tries   = 2;

    public function __construct(
        private string $code,
        private string $cacheKey
    ) {}

    public function handle(CodeReviewService $service): void
    {
        $result = $service-&amp;gt;review($this-&amp;gt;code);
        Cache::put($this-&amp;gt;cacheKey, $result, now()-&amp;gt;addMinutes(10));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the controller to dispatch the job and add a polling method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function review(Request $request)
{
    $request-&amp;gt;validate([&#39;code&#39; =&amp;gt; &#39;required|string|min:10|max:5000&#39;]);

    $key = &#39;review_&#39; . md5($request-&amp;gt;input(&#39;code&#39;) . uniqid());

    ProcessCodeReview::dispatch($request-&amp;gt;input(&#39;code&#39;), $key);

    return view(&#39;code-review.waiting&#39;, [&#39;cacheKey&#39; =&amp;gt; $key]);
}

public function poll(string $key)
{
    $feedback = Cache::get($key);

    if (!$feedback) {
        return response()-&amp;gt;json([&#39;status&#39; =&amp;gt; &#39;pending&#39;]);
    }

    return response()-&amp;gt;json([&#39;status&#39; =&amp;gt; &#39;done&#39;, &#39;feedback&#39; =&amp;gt; $feedback]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
For local development, set &lt;code&gt;QUEUE_CONNECTION=sync&lt;/code&gt; in your &lt;code&gt;.env&lt;/code&gt; and jobs will run immediately without needing a worker. In production use &lt;code&gt;redis&lt;/code&gt; or &lt;code&gt;database&lt;/code&gt;.
&lt;/p&gt;
&lt;h2&gt;What the bot actually catching: A Real Example&lt;/h2&gt;
&lt;p&gt;
Here is a piece of code I have seen in various forms across different projects. It works. On a test database with ten orders nobody notices anything wrong with it.
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

public function getUserOrders($userId)
{
    $orders = DB::table(&#39;orders&#39;)-&amp;gt;where(&#39;user_id&#39;, $userId)-&amp;gt;get();

    foreach ($orders as $order) {
        $items = DB::table(&#39;order_items&#39;)-&amp;gt;where(&#39;order_id&#39;, $order-&amp;gt;id)-&amp;gt;get();
        echo $order-&amp;gt;id . &#39;: &#39; . count($items) . &#39; items&amp;lt;br&amp;gt;&#39;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Put this in front of a customer with 400 orders and watch what happens to your database.
&lt;/p&gt;
&lt;p&gt;Paste that into the reviewer and here is what comes back:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;summary&quot;: &quot;Code is functional but has a critical N+1 query problem and bypasses Eloquent entirely. Direct output with echo also breaks MVC separation and makes this code untestable.&quot;,
  &quot;score&quot;: 3,
  &quot;issues&quot;: [
    {
      &quot;severity&quot;: &quot;critical&quot;,
      &quot;line_hint&quot;: &quot;foreach loop, DB::table order_items&quot;,
      &quot;message&quot;: &quot;N+1 query problem. One database query fires per order inside the loop. With 400 orders that becomes 401 queries. Use Eloquent with eager loading: Order::with(&#39;items&#39;)-&gt;where(&#39;user_id&#39;, $userId)-&gt;get()&quot;
    },
    {
      &quot;severity&quot;: &quot;warning&quot;,
      &quot;line_hint&quot;: &quot;DB::table()&quot;,
      &quot;message&quot;: &quot;Raw query builder bypasses Eloquent model logic, accessors, and relationships. Switching to Eloquent models makes the code significantly easier to maintain and test.&quot;
    },
    {
      &quot;severity&quot;: &quot;info&quot;,
      &quot;line_hint&quot;: &quot;echo statement&quot;,
      &quot;message&quot;: &quot;Direct output inside a controller or service method violates MVC. Return structured data and handle rendering in the view layer.&quot;
    }
  ],
  &quot;suggestions&quot;: [
    &quot;Define a hasMany relationship on Order pointing to OrderItem.&quot;,
    &quot;Replace DB::table calls with Order::with(&#39;items&#39;)-&gt;where(&#39;user_id&#39;, $userId)-&gt;get()&quot;,
    &quot;Return a collection and let Blade handle the output, do not echo from service methods.&quot;
  ],
  &quot;security_flags&quot;: [
    &quot;$userId passes into a query with no type check or validation. Confirm this is an authenticated, validated integer before it reaches any DB call.&quot;
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
Score of 3, one critical issue, one warning, one info note, and a security flag. All accurate, all actionable. That took under four seconds and it is exactly the kind of feedback that usually takes a few minutes of a senior developer&#39;s time to write out properly.
&lt;/p&gt;
&lt;h2&gt;Where this fits in an actual Workflow&lt;/h2&gt;
&lt;p&gt;
I want to be direct about this because I have seen people set up tools like this and then either over-rely on them or drop them after two weeks. The right use here is as a first-pass gate, not a replacement for peer review.
&lt;/p&gt;
&lt;p&gt;
The workflow that actually makes sense: developer opens a PR, the bot triggers via a GitHub webhook, posts its feedback as a comment on the PR, and the human reviewer knows the basics have already been handled. They skip straight to the parts that need real judgment, design decisions, edge cases, whether the approach fits the broader architecture.
&lt;/p&gt;
&lt;p&gt;
That is where this earns its place. Not by replacing review. By removing the repetitive first ten minutes of it.
&lt;/p&gt;
&lt;h2&gt;Few things to know before building this:&lt;/h2&gt;
&lt;p&gt;
The prompt structure matters more than anything else in this whole build. Early versions I tried came back as freeform text, which is hard to work with in a UI. Asking the model to return only JSON with field names you define upfront makes parsing reliable every time. Do not skip that part.
&lt;/p&gt;
&lt;p&gt;
GPT-4o is noticeably better than GPT-3.5 for this kind of task, not just in accuracy but in how it explains problems. &quot;Use eager loading&quot; is less useful than &quot;this fires one query per iteration, here is the exact fix.&quot; The difference in API cost is worth it if you are using this on a real codebase.
&lt;/p&gt;
&lt;p&gt;
One more thing. Do not feed entire files in at once, at least not to start. Keep the input focused: a single method, one class, a specific feature. Smaller focused reviews produce better feedback. You can extend the input limit later once you are happy with the output quality.
&lt;/p&gt;
&lt;p&gt;
From here the natural extensions to build are a GitHub webhook integration to trigger reviews on every PR automatically, a review history table to track quality trends over time, custom system prompts per project so the bot reviews against your team&#39;s conventions specifically, and Slack notifications when a review completes. None of that is complicated to add on top of what we have built here.
&lt;/p&gt;
&lt;p&gt;
If you found this useful, drop a comment in the comment section.
&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/03/ai-code-review-bot-laravel.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-5799513366160715022</guid><pubDate>Wed, 18 Mar 2026 21:04:00 +0000</pubDate><atom:updated>2026-03-18T14:04:00.038-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><title>Building a RAG System in Laravel from Scratch</title><description>&lt;p&gt;Most RAG tutorials start with &quot;first, sign up for Pinecone.&quot; I&#39;m going to skip that entirely. For the majority of Laravel applications, a dedicated vector database is overkill. You already have MySQL. You already have Laravel&#39;s queue system. That&#39;s enough to build a fully functional retrieval augmented generation pipeline that works well into the tens of thousands of documents.&lt;/p&gt;
&lt;p&gt;RAG solves a specific problem. LLMs are trained on general data up to a cutoff date. They know nothing about your application&#39;s content, your internal docs, your product knowledge base, or anything else specific to your domain. RAG fixes this by retrieving relevant content from your own data and injecting it into the prompt as context before asking the model to answer. The model stops guessing and starts answering based on what you actually have.&lt;/p&gt;
&lt;p&gt;Here is how to build it properly in Laravel.&lt;/p&gt;
&lt;h2&gt;What We Are Building&lt;/h2&gt;
&lt;p&gt;A pipeline that does four things:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Accepts documents (articles, pages, PDFs, anything text-based) and stores them with their embeddings&lt;/li&gt;
    &lt;li&gt;When a user asks a question, converts that question into an embedding&lt;/li&gt;
    &lt;li&gt;Finds the most semantically similar documents using cosine similarity against your stored embeddings&lt;/li&gt;
    &lt;li&gt;Feeds those documents as context to GPT and returns a grounded answer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No external services beyond OpenAI. No Docker containers for a vector DB. Just Laravel, MySQL, and two API calls per query.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Laravel 10 or 11&lt;/li&gt;
    &lt;li&gt;PHP 8.1+&lt;/li&gt;
    &lt;li&gt;MySQL 8.0+&lt;/li&gt;
    &lt;li&gt;OpenAI API key&lt;/li&gt;
    &lt;li&gt;Guzzle (ships with Laravel)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: The Documents Table&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan make:migration create_documents_table
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        public function up(): void
        {
            Schema::create(&#39;documents&#39;, function (Blueprint $table) {
                $table-&gt;id();
                $table-&gt;string(&#39;title&#39;);
                $table-&gt;longText(&#39;content&#39;);
                $table-&gt;longText(&#39;embedding&#39;)-&gt;nullable(); // JSON float array
                $table-&gt;string(&#39;source&#39;)-&gt;nullable(); // URL, filename, etc.
                $table-&gt;timestamps();
            });
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan migrate
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The embedding column stores a JSON-encoded array of 1536 floats (for text-embedding-3-small). Yes, it&#39;s a text column, not a native vector type. MySQL 9 adds vector support but for now JSON in a longText column works fine for most use cases.&lt;/p&gt;
&lt;h2&gt;Step 2: The Document Model&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan make:model Document
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Models;

        use Illuminate\Database\Eloquent\Model;

        class Document extends Model
        {
            protected $fillable = [&#39;title&#39;, &#39;content&#39;, &#39;embedding&#39;, &#39;source&#39;];

            protected $casts = [
                &#39;embedding&#39; =&gt; &#39;array&#39;,
            ];
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;embedding&lt;/strong&gt; cast handles the JSON encoding and decoding automatically. When you set &lt;strong&gt;$document-&gt;embedding = $vectorArray&lt;/strong&gt;, Laravel serializes it. When you read it back, you get a PHP array of floats.&lt;/p&gt;
&lt;h2&gt;Step 3: The Embedding Service&lt;/h2&gt;
&lt;p&gt;Keep all OpenAI communication in one place. This makes it easy to swap providers later.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan make:service EmbeddingService
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Services;

        use Illuminate\Support\Facades\Http;

        class EmbeddingService
        {
            private string $apiKey;
            private string $model = &#39;text-embedding-3-small&#39;;

            public function __construct()
            {
                $this-&gt;apiKey = config(&#39;services.openai.key&#39;);
            }

            public function embed(string $text): array
            {
                // Trim to ~8000 tokens to stay within model limits
                $text = mb_substr(strip_tags($text), 0, 32000);

                $response = Http::withToken($this-&gt;apiKey)
                    -&gt;post(&#39;https://api.openai.com/v1/embeddings&#39;, [
                        &#39;model&#39; =&gt; $this-&gt;model,
                        &#39;input&#39; =&gt; $text,
                    ]);

                if ($response-&gt;failed()) {
                    throw new \RuntimeException(&#39;OpenAI embedding request failed: &#39; . $response-&gt;body());
                }

                return $response-&gt;json(&#39;data.0.embedding&#39;);
            }

            public function cosineSimilarity(array $a, array $b): float
            {
                $dot = 0.0;
                $magA = 0.0;
                $magB = 0.0;

                foreach ($a as $i =&gt; $val) {
                    $dot += $val * $b[$i];
                    $magA += $val ** 2;
                    $magB += $b[$i] ** 2;
                }

                $denominator = sqrt($magA) * sqrt($magB);

                return $denominator &gt; 0 ? $dot / $denominator : 0.0;
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Register it in &lt;strong&gt;config/services.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        &#39;openai&#39; =&gt; [
            &#39;key&#39; =&gt; env(&#39;OPENAI_API_KEY&#39;),
        ],
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 4: Indexing Documents&lt;/h2&gt;
&lt;p&gt;A command to process documents and store their embeddings. You run this once on existing content, then hook it into your document creation flow going forward.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan make:command IndexDocuments
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Console\Commands;

        use App\Models\Document;
        use App\Services\EmbeddingService;
        use Illuminate\Console\Command;

        class IndexDocuments extends Command
        {
            protected $signature = &#39;rag:index {--fresh : Re-index all documents}&#39;;
            protected $description = &#39;Generate and store embeddings for all documents&#39;;

            public function handle(EmbeddingService $embedder): int
            {
                $query = Document::query();

                if (!$this-&gt;option(&#39;fresh&#39;)) {
                    $query-&gt;whereNull(&#39;embedding&#39;);
                }

                $documents = $query-&gt;get();
                $bar = $this-&gt;output-&gt;createProgressBar($documents-&gt;count());

                foreach ($documents as $doc) {
                    try {
                        $doc-&gt;embedding = $embedder-&gt;embed($doc-&gt;title . &quot;\n\n&quot; . $doc-&gt;content);
                        $doc-&gt;save();
                        $bar-&gt;advance();
                    } catch (\Exception $e) {
                        $this-&gt;error(&quot;Failed on document {$doc-&gt;id}: &quot; . $e-&gt;getMessage());
                    }

                    // Respect OpenAI rate limits
                    usleep(200000); // 200ms between requests
                }

                $bar-&gt;finish();
                $this-&gt;newLine();
                $this-&gt;info(&#39;Indexing complete.&#39;);

                return self::SUCCESS;
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Run it:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan rag:index
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Notice I&#39;m concatenating title and content before embedding. The title carries a lot of semantic weight and including it improves retrieval accuracy noticeably.&lt;/p&gt;
&lt;h2&gt;Step 5: The Retrieval Logic&lt;/h2&gt;
&lt;p&gt;This is the core of RAG. Given a query, find the most relevant documents.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Services;

        use App\Models\Document;

        class RetrievalService
        {
            public function __construct(private EmbeddingService $embedder) {}

            public function retrieve(string $query, int $topK = 5, float $threshold = 0.75): array
            {
                $queryVector = $this-&gt;embedder-&gt;embed($query);
                $documents = Document::whereNotNull(&#39;embedding&#39;)-&gt;get();

                $scored = $documents-&gt;map(function (Document $doc) use ($queryVector) {
                    return [
                        &#39;document&#39; =&gt; $doc,
                        &#39;score&#39; =&gt; $this-&gt;embedder-&gt;cosineSimilarity($queryVector, $doc-&gt;embedding),
                    ];
                })
                -&gt;filter(fn($item) =&gt; $item[&#39;score&#39;] &gt;= $threshold)
                -&gt;sortByDesc(&#39;score&#39;)
                -&gt;take($topK)
                -&gt;values();

                return $scored-&gt;toArray();
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;$threshold&lt;/strong&gt; of 0.75 filters out loosely related documents. You may need to tune this for your content, lower it if you&#39;re getting no results, raise it if you&#39;re getting irrelevant ones. Anywhere between 0.70 and 0.85 is usually sensible.&lt;/p&gt;
&lt;h2&gt;Step 6: The RAG Query Service&lt;/h2&gt;
&lt;p&gt;This ties retrieval and generation together.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Services;

        use Illuminate\Support\Facades\Http;

        class RagService
        {
            public function __construct(
                private RetrievalService $retriever,
                private string $apiKey
            ) {
                $this-&gt;apiKey = config(&#39;services.openai.key&#39;);
            }

            public function ask(string $question): array
            {
                // Step 1: Retrieve relevant documents
                $results = $this-&gt;retriever-&gt;retrieve($question, topK: 4);

                if (empty($results)) {
                    return [
                        &#39;answer&#39; =&gt; &#39;I could not find relevant information to answer this question.&#39;,
                        &#39;sources&#39; =&gt; [],
                    ];
                }

                // Step 2: Build context from retrieved docs
                $context = collect($results)
                    -&gt;map(fn($r) =&gt; &quot;### {$r[&#39;document&#39;]-&gt;title}\n{$r[&#39;document&#39;]-&gt;content}&quot;)
                    -&gt;join(&quot;\n\n---\n\n&quot;);

                // Step 3: Send to GPT with context
                $response = Http::withToken($this-&gt;apiKey)
                    -&gt;post(&#39;https://api.openai.com/v1/chat/completions&#39;, [
                        &#39;model&#39; =&gt; &#39;gpt-4o-mini&#39;,
                        &#39;temperature&#39; =&gt; 0.2,
                        &#39;messages&#39; =&gt; [
                            [
                                &#39;role&#39; =&gt; &#39;system&#39;,
                                &#39;content&#39; =&gt; &quot;You are a helpful assistant. Answer questions using only the context provided below. If the answer is not in the context, say so clearly. Do not make up information.\n\nContext:\n{$context}&quot;
                            ],
                            [
                                &#39;role&#39; =&gt; &#39;user&#39;,
                                &#39;content&#39; =&gt; $question,
                            ]
                        ],
                    ]);

                return [
                    &#39;answer&#39; =&gt; $response-&gt;json(&#39;choices.0.message.content&#39;),
                    &#39;sources&#39; =&gt; collect($results)-&gt;map(fn($r) =&gt; [
                        &#39;title&#39; =&gt; $r[&#39;document&#39;]-&gt;title,
                        &#39;source&#39; =&gt; $r[&#39;document&#39;]-&gt;source,
                        &#39;score&#39; =&gt; round($r[&#39;score&#39;], 3),
                    ])-&gt;toArray(),
                ];
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Two things worth noting here. Temperature is set to 0.2, not the default 0.7. You want deterministic, factual answers when doing RAG, not creative ones. And the system prompt explicitly tells the model to stay within the provided context and admit when it doesn&#39;t know. Without that instruction, GPT will hallucinate rather than say &quot;I don&#39;t have that information.&quot;&lt;/p&gt;
&lt;h2&gt;Step 7: The Controller&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan make:controller RagController
    &lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Http\Controllers;

        use App\Services\RagService;
        use Illuminate\Http\Request;

        class RagController extends Controller
        {
            public function __construct(private RagService $rag) {}

            public function ask(Request $request)
            {
                $request-&gt;validate([&#39;question&#39; =&gt; &#39;required|string|max:500&#39;]);

                $result = $this-&gt;rag-&gt;ask($request-&gt;input(&#39;question&#39;));

                return response()-&gt;json($result);
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Register the route in &lt;strong&gt;routes/api.php&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        Route::post(&#39;/ask&#39;, [RagController::class, &#39;ask&#39;]);
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 8: Test It&lt;/h2&gt;
&lt;p&gt;Seed a couple of documents first:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        Document::create([
            &#39;title&#39; =&gt; &#39;Laravel Queue Configuration&#39;,
            &#39;content&#39; =&gt; &#39;Laravel queues allow you to defer time-consuming tasks...&#39;,
            &#39;source&#39; =&gt; &#39;https://laravel.com/docs/queues&#39;,
        ]);
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Run the indexer:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php artisan rag:index
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Then hit the endpoint:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        curl -X POST http://your-app.test/api/ask \
            -H &quot;Content-Type: application/json&quot; \
            -d &#39;{&quot;question&quot;: &quot;How do I configure Laravel queues?&quot;}&#39;
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Response:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        {
            &quot;answer&quot;: &quot;Laravel queues are configured via the config/queue.php file...&quot;,
            &quot;sources&quot;: [
                {
                &quot;title&quot;: &quot;Laravel Queue Configuration&quot;,
                &quot;source&quot;: &quot;https://laravel.com/docs/queues&quot;,
                &quot;score&quot;: 0.891
                }
            ]
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Where This Falls Down at Scale&lt;/h2&gt;
&lt;p&gt;This setup works well up to roughly 50,000 documents. Beyond that, loading all embeddings into memory for comparison becomes a problem. At that point your options are:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Add a MySQL generated column + raw SQL dot product approximation to filter candidates before full cosine comparison&lt;/li&gt;
    &lt;li&gt;Move to pgvector if you can switch to PostgreSQL, which handles this natively and efficiently&lt;/li&gt;
    &lt;li&gt;Then and only then consider Pinecone or Weaviate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most Laravel projects never reach that threshold. Start simple, measure, then scale the storage layer when you actually need to.&lt;/p&gt;
&lt;h2&gt;What to Build on Top of This&lt;/h2&gt;
&lt;p&gt;Once the core pipeline is working, the useful next steps are: caching query embeddings so repeated questions don&#39;t hit the API twice, chunking long documents into 500-token segments before embedding so retrieval is more granular, adding a feedback mechanism so users can flag bad answers and you can track retrieval quality over time, and per-user conversation history so the model has context across multiple turns.&lt;/p&gt;
&lt;p&gt;That is a production-ready RAG foundation in Laravel with no external vector database. The whole thing is maybe 200 lines of actual PHP spread across four service classes and one command.&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2026/03/building-rag-system-in-laravel-from-scratch.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-354698062168718739</guid><pubDate>Tue, 27 Jan 2026 13:56:00 +0000</pubDate><atom:updated>2026-03-18T12:52:49.345-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Wordpress</category><title>AI SEO Content Quality Analyzer for WordPress Using PHP and OpenAI</title><description>&lt;p&gt;I&#39;ve used Yoast and Rank Math for years. Both are solid, but they kept telling me what was wrong, keyword density too low, meta too long without ever telling me why the content wasn&#39;t ranking. So I built something different.&lt;/p&gt;
&lt;p&gt;This tutorial walks through building a WordPress plugin that uses OpenAI to analyze your post content the way a search engine actually thinks about it, checking search intent alignment, content depth, and semantic quality then gives you a score and specific suggestions right inside the post editor.&lt;/p&gt;
&lt;p&gt;Once installed, the plugin analyzes each post and:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Analyze the alignment of search intent&lt;/li&gt;
    &lt;li&gt;Verify the content&#39;s completeness&lt;/li&gt;
    &lt;li&gt;Identify generic or thin sections&lt;/li&gt;
    &lt;li&gt;Examine the quality of semantic SEO&lt;/li&gt;
    &lt;li&gt;Make suggestions for headings, FAQs, and enhancements&lt;/li&gt;
    &lt;li&gt;Give an AI SEO score between 0 and 100.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is much more advanced than conventional SEO plugins.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;WordPress 6.x&lt;/li&gt;
    &lt;li&gt;PHP 8.1+&lt;/li&gt;
    &lt;li&gt;Composer (optional)&lt;/li&gt;
    &lt;li&gt;An OpenAI API key&lt;/li&gt;
    &lt;li&gt;Basic WordPress plugin development knowledge&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: Create the WordPress Plugin&lt;/h2&gt;
&lt;p&gt;Create a new plugin folder:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        wp-content/plugins/ai-seo-analyzer/
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Create &lt;strong&gt;&lt;i&gt;ai-seo-analyzer.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        /**
        * Plugin Name: AI SEO Content Quality Analyzer
        * Description: Analyzes WordPress content quality using AI and provides SEO recommendations.
        * Version: 1.0.0
        * Author: phpcmsframework.com
        */

        if (!defined(&#39;ABSPATH&#39;)) exit;
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Activate the plugin from WP Admin → Plugins.&lt;/p&gt;
&lt;h2&gt;Step 2: Add Meta Box in Post Editor&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        add_action(&#39;add_meta_boxes&#39;, function () {
            add_meta_box(
                &#39;ai_seo_box&#39;,
                &#39;AI SEO Content Analyzer&#39;,
                &#39;ai_seo_meta_box&#39;,
                [&#39;post&#39;, &#39;page&#39;],
                &#39;side&#39;,
                &#39;high&#39;
            );
        });

        function ai_seo_meta_box($post)
        {
            echo &#39;&amp;lt;button class=&quot;button button-primary&quot; id=&quot;ai-seo-analyze&quot;&amp;gt;Analyze Content&amp;lt;/button&amp;gt;&#39;;
            echo &#39;&amp;lt;div id=&quot;ai-seo-result&quot; style=&quot;margin-top:10px;&quot;&amp;gt;&amp;lt;/div&amp;gt;&#39;;
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 3: AJAX Handler (PHP Only)&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        add_action(&#39;wp_ajax_ai_seo_analyze&#39;, &#39;ai_seo_analyze&#39;);

        function ai_seo_analyze()
        {
            $postId = intval($_POST[&#39;post_id&#39;]);
            $post = get_post($postId);

            if (!$post) {
                wp_send_json_error(&#39;Post not found&#39;);
            }

            $analysis = ai_seo_analyze_content($post-&gt;post_title, $post-&gt;post_content);

            wp_send_json_success($analysis);
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 4: AI Content Analysis Function&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        function ai_seo_analyze_content($title, $content)
        {
            $prompt = &quot;
        Analyze the SEO quality of the following content.

        Return JSON with:
        - seo_score (0-100)
        - intent_match (Good/Average/Poor)
        - strengths (list)
        - weaknesses (list)
        - improvement_suggestions (list)
        - suggested_headings
        - suggested_faqs

        Title:
        $title

        Content:
        &quot; . strip_tags($content);

            $payload = [
                &#39;model&#39; =&gt; &#39;gpt-4o-mini&#39;,
                &#39;messages&#39; =&gt; [
                    [&#39;role&#39; =&gt; &#39;system&#39;, &#39;content&#39; =&gt; &#39;You are an expert SEO auditor.&#39;],
                    [&#39;role&#39; =&gt; &#39;user&#39;, &#39;content&#39; =&gt; $prompt]
                ],
                &#39;temperature&#39; =&gt; 0.2
            ];

            $ch = curl_init(&#39;https://api.openai.com/v1/chat/completions&#39;);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER =&gt; true,
                CURLOPT_HTTPHEADER =&gt; [
                    &#39;Content-Type: application/json&#39;,
                    &#39;Authorization: Bearer &#39; . getenv(&#39;OPENAI_API_KEY&#39;)
                ],
                CURLOPT_POST =&gt; true,
                CURLOPT_POSTFIELDS =&gt; json_encode($payload)
            ]);

            $response = json_decode(curl_exec($ch), true);
            curl_close($ch);

            return json_decode($response[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;], true);
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 5: JavaScript for Admin UI&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        add_action(&#39;admin_footer&#39;, function () {
        ?&amp;gt;
        &amp;lt;script&amp;gt;
        jQuery(function ($) {
            $(&#39;#ai-seo-analyze&#39;).on(&#39;click&#39;, function () {
                $(&#39;#ai-seo-result&#39;).html(&#39;Analyzing...&#39;);
                $.post(ajaxurl, {
                    action: &#39;ai_seo_analyze&#39;,
                    post_id: $(&#39;#post_ID&#39;).val()
                }, function (res) {
                    if (res.success) {
                        let r = res.data;
                        $(&#39;#ai-seo-result&#39;).html(`
                            &amp;lt;strong&amp;gt;SEO Score:&amp;lt;/strong&amp;gt; ${r.seo_score}/100&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
                            &amp;lt;strong&amp;gt;Strengths:&amp;lt;/strong&amp;gt;&amp;lt;ul&gt;${r.strengths.map(i =&amp;gt; `&amp;lt;li&amp;gt;${i}&amp;lt;/li&amp;gt;`).join(&#39;&#39;)}&amp;lt;/ul&amp;gt;
                            &amp;lt;strong&amp;gt;Weaknesses:&amp;lt;/strong&amp;gt;&amp;lt;ul&amp;gt;${r.weaknesses.map(i =&gt; `&amp;lt;li&amp;gt;${i}&amp;lt;/li&amp;gt;`).join(&#39;&#39;)}&amp;lt;/ul&amp;gt;
                        `);
                    } else {
                        $(&#39;#ai-seo-result&#39;).html(&#39;Error analyzing content.&#39;);
                    }
                });
            });
        });
        &lt;/script&gt;
        &amp;lt;?php
        });
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;How It Works (Editor View)&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Open any post or page&lt;/li&gt;
    &lt;li&gt;Click “&lt;strong&gt;Analyze Content&lt;/strong&gt;”&lt;/li&gt;
    &lt;li&gt;AI reviews search intent, depth, structure&lt;/li&gt;
    &lt;li&gt;You get a quality score + fixes&lt;/li&gt;
    &lt;li&gt;Update content → re-analyze&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Smart Enhancements You Can Add&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Compare against top-ranking competitor URLs&lt;/li&gt;
    &lt;li&gt;Detect keyword stuffing vs natural language&lt;/li&gt;
    &lt;li&gt;Analyze internal linking opportunities&lt;/li&gt;
    &lt;li&gt;Auto-generate missing sections&lt;/li&gt;
    &lt;li&gt;Save score history per post&lt;/li&gt;
    &lt;li&gt;Bulk audit via WP-CLI&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Security &amp; Performance Notes&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Store API key in wp-config.php&lt;/li&gt;
    &lt;li&gt;Limit analysis frequency&lt;/li&gt;
    &lt;li&gt;Strip shortcodes before sending content&lt;/li&gt;
    &lt;li&gt;Cache analysis results&lt;/li&gt;
    &lt;li&gt;Use nonces for AJAX calls in production&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2026/01/ai-seo-content-quality-analyzer-for-wordpress-with-php-openai.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-3275149354823759869</guid><pubDate>Mon, 29 Dec 2025 14:38:00 +0000</pubDate><atom:updated>2026-03-18T13:13:39.196-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Symfony</category><title>AI Duplicate Content Detector for Symfony Using PHP and OpenAI Embeddings</title><description>&lt;p&gt;If you&#39;ve been running a Symfony-based blog or CMS for a while, chances are you already have duplicate content. You just don&#39;t know it yet. Editors rewrite old articles, documentation pages grow organically, and over time you end up with five pages that all basically say the same thing, just worded differently.&lt;/p&gt;
&lt;p&gt;The usual approach to catching this, string matching or exact text comparison, falls apart the moment someone changes a few words. Two articles can be 90% the same in meaning and a simple diff won&#39;t flag either of them.&lt;/p&gt;
&lt;p&gt;That&#39;s where OpenAI embeddings come in. Instead of comparing words, we compare meaning. In this tutorial, I&#39;ll show you how to build a duplicate content detector in Symfony that uses vector embeddings and cosine similarity to catch semantically similar articles, even when the wording is completely different..&lt;/p&gt;
&lt;h2&gt;What We&#39;re Constructing&lt;/h2&gt;
&lt;p&gt;After completing this guide, you will have:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;AI-produced embeddings for every article&lt;/li&gt;
    &lt;li&gt;A cosine similarity-based semantic similarity checker&lt;/li&gt;
    &lt;li&gt;A command for the console to find duplicates&lt;/li&gt;
    &lt;li&gt;A threshold for similarity (e.g., 85%+) to mark content&lt;/li&gt;
    &lt;li&gt;Any Symfony CMS can be integrated with this foundation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is effective for:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Blogs&lt;/li&gt;
    &lt;li&gt;Knowledge bases&lt;/li&gt;
    &lt;li&gt;Portals for documentation&lt;/li&gt;
    &lt;li&gt;Pages with e-commerce content&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Symfony 6 or 7&lt;/li&gt;
    &lt;li&gt;PHP 8.1+&lt;/li&gt;
    &lt;li&gt;Doctrine ORM&lt;/li&gt;
    &lt;li&gt;MySQL / PostgreSQL&lt;/li&gt;
    &lt;li&gt;An OpenAI API key&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: Add an Embedding Column to Your Entity&lt;/h2&gt;
&lt;p&gt;Assume an Article entity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;i&gt;src/Entity/Article.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        #[ORM\Column(type: &#39;json&#39;, nullable: true)]
        private ?array $embedding = null;

        public function getEmbedding(): ?array
        {
            return $this-&gt;embedding;
        }

        public function setEmbedding(?array $embedding): self
        {
            $this-&gt;embedding = $embedding;
            return $this;
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Create and run migration:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php bin/console make:migration
        php bin/console doctrine:migrations:migrate
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 2: Generate Embeddings for Articles&lt;/h2&gt;
&lt;p&gt;Create a Symfony command:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php bin/console make:command app:generate-article-embeddings
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;i&gt;GenerateArticleEmbeddingsCommand.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Command;

        use App\Entity\Article;
        use Doctrine\ORM\EntityManagerInterface;
        use Symfony\Component\Console\Command\Command;
        use Symfony\Component\Console\Input\InputInterface;
        use Symfony\Component\Console\Output\OutputInterface;

        class GenerateArticleEmbeddingsCommand extends Command
        {
            protected static $defaultName = &#39;app:generate-article-embeddings&#39;;

            public function __construct(
                private EntityManagerInterface $em,
                private string $apiKey
            ) {
                parent::__construct();
            }

            protected function execute(InputInterface $input, OutputInterface $output): int
            {
                $articles = $this-&gt;em-&gt;getRepository(Article::class)-&gt;findAll();

                foreach ($articles as $article) {
                    if ($article-&gt;getEmbedding()) {
                        continue;
                    }

                    $embedding = $this-&gt;getEmbedding(
                        strip_tags($article-&gt;getContent())
                    );

                    $article-&gt;setEmbedding($embedding);
                    $this-&gt;em-&gt;persist($article);

                    $output-&gt;writeln(&quot;Embedding generated for article ID {$article-&gt;getId()}&quot;);
                }

                $this-&gt;em-&gt;flush();
                return Command::SUCCESS;
            }

            private function getEmbedding(string $text): array
            {
                $payload = [
                    &#39;model&#39; =&gt; &#39;text-embedding-3-small&#39;,
                    &#39;input&#39; =&gt; mb_substr($text, 0, 4000)
                ];

                $ch = curl_init(&#39;https://api.openai.com/v1/embeddings&#39;);
                curl_setopt_array($ch, [
                    CURLOPT_RETURNTRANSFER =&gt; true,
                    CURLOPT_HTTPHEADER =&gt; [
                        &quot;Content-Type: application/json&quot;,
                        &quot;Authorization: Bearer {$this-&gt;apiKey}&quot;
                    ],
                    CURLOPT_POST =&gt; true,
                    CURLOPT_POSTFIELDS =&gt; json_encode($payload)
                ]);

                $response = curl_exec($ch);
                curl_close($ch);

                return json_decode($response, true)[&#39;data&#39;][0][&#39;embedding&#39;] ?? [];
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Store the API key in &lt;strong&gt;.env.local&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        OPENAI_API_KEY=your_key_here
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 3: Cosine Similarity Helper&lt;/h2&gt;
&lt;p&gt;Create a reusable helper.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;i&gt;src/Service/SimilarityService.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Service;

        class SimilarityService
        {
            public function cosine(array $a, array $b): float
            {
                $dot = 0;
                $magA = 0;
                $magB = 0;

                foreach ($a as $i =&gt; $val) {
                    $dot += $val * $b[$i];
                    $magA += $val ** 2;
                    $magB += $b[$i] ** 2;
                }

                return $dot / (sqrt($magA) * sqrt($magB));
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 4: Detect Duplicate Articles&lt;/h2&gt;
&lt;p&gt;Create another command:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        php bin/console make:command app:detect-duplicates
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;i&gt;DetectDuplicateContentCommand.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        namespace App\Command;

        use App\Entity\Article;
        use App\Service\SimilarityService;
        use Doctrine\ORM\EntityManagerInterface;
        use Symfony\Component\Console\Command\Command;
        use Symfony\Component\Console\Input\InputInterface;
        use Symfony\Component\Console\Output\OutputInterface;

        class DetectDuplicateContentCommand extends Command
        {
            protected static $defaultName = &#39;app:detect-duplicates&#39;;

            public function __construct(
                private EntityManagerInterface $em,
                private SimilarityService $similarity
            ) {
                parent::__construct();
            }

            protected function execute(InputInterface $input, OutputInterface $output): int
            {
                $articles = $this-&gt;em-&gt;getRepository(Article::class)-&gt;findAll();
                $threshold = 0.85;

                foreach ($articles as $i =&gt; $a) {
                    foreach ($articles as $j =&gt; $b) {
                        if ($j &lt;= $i) continue;
                        if (!$a-&gt;getEmbedding() || !$b-&gt;getEmbedding()) continue;

                        $score = $this-&gt;similarity-&gt;cosine(
                            $a-&gt;getEmbedding(),
                            $b-&gt;getEmbedding()
                        );

                        if ($score &gt;= $threshold) {
                            $output-&gt;writeln(
                                sprintf(
                                    &quot;⚠ Duplicate detected (%.2f): Article %d and %d&quot;,
                                    $score,
                                    $a-&gt;getId(),
                                    $b-&gt;getId()
                                )
                            );
                        }
                    }
                }

                return Command::SUCCESS;
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 5: Run via Cron (Optional)&lt;/h2&gt;
&lt;p&gt;To scan regularly, add a cron job:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        0 2 * * * php /path/to/project/bin/console app:detect-duplicates
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;You can store results in a table or send email notifications.&lt;/p&gt;
&lt;h2&gt;Example Output&lt;/h2&gt;
&lt;pre&gt;
    &lt;code&gt;
        Duplicate detected (0.91): Article 12 and 37
        Duplicate detected (0.88): Article 18 and 44
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Useful Improvements&lt;/h2&gt;
&lt;p&gt;This system can be expanded with:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Admin UI for reviewing duplicates&lt;/li&gt;
    &lt;li&gt;Canonical page suggestions automatically&lt;/li&gt;
    &lt;li&gt;Weighting of the title and excerpt&lt;/li&gt;
    &lt;li&gt;Similarity detection at the section level&lt;/li&gt;
    &lt;li&gt;Using Messenger for batch processing&lt;/li&gt;
    &lt;li&gt;Large-scale vector databases&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cost &amp; Performance Advice&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Create embeddings for each article only once.&lt;/li&gt;
    &lt;li&gt;Before embedding, limit the length of the content.&lt;/li&gt;
    &lt;li&gt;Ignore the draft content&lt;/li&gt;
    &lt;li&gt;Cache similarity findings&lt;/li&gt;
    &lt;li&gt;For big datasets, use queues.&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/12/ai-duplicate-content-detector-for-symfony-with-php-openai.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-929579979132108025</guid><pubDate>Tue, 23 Dec 2025 05:49:00 +0000</pubDate><atom:updated>2026-03-18T13:16:03.082-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>AI Category Recommendation System for Drupal 11 Using PHP and OpenAI</title><description>&lt;p&gt;Categorization in Drupal is one of those things that looks fine on the surface but gets messy fast. Editors are busy, categories get picked in a hurry, and before long you&#39;ve got a dozen articles filed under the wrong taxonomy term or spread inconsistently across three different ones that mean almost the same thing.&lt;/p&gt;
&lt;p&gt;The fix isn&#39;t enforcing stricter rules on editors. It&#39;s removing the guesswork entirely.&lt;/p&gt;
&lt;p&gt;In this tutorial, I&#39;ll walk you through building a custom Drupal 11 module that reads a node&#39;s actual content and uses OpenAI to pick the most appropriate category automatically, no manual selection needed.&lt;/p&gt;
&lt;p&gt;It hooks into the node save process, pulls your existing taxonomy terms, and asks the AI to match the content against them. The result gets assigned before the node is stored. It&#39;s a small module but it solves a real problem, especially on sites with large editorial teams or high publishing volume.&lt;/p&gt;
&lt;h2&gt;What This Module Will Do&lt;/h2&gt;
&lt;p&gt;Our AI category system will:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Analyze node body content on save&lt;/li&gt;
    &lt;li&gt;Compare it against existing taxonomy terms&lt;/li&gt;
    &lt;li&gt;Recommend the most relevant category&lt;/li&gt;
    &lt;li&gt;Automatically assign it (or display it to editors)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use cases include:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Blog posts&lt;/li&gt;
    &lt;li&gt;Documentation pages&lt;/li&gt;
    &lt;li&gt;News articles&lt;/li&gt;
    &lt;li&gt;Knowledge bases&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Make sure you have:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Drupal 11&lt;/li&gt;
    &lt;li&gt;PHP 8.1+&lt;/li&gt;
    &lt;li&gt;Composer&lt;/li&gt;
    &lt;li&gt;A taxonomy vocabulary (example: categories)&lt;/li&gt;
    &lt;li&gt;An OpenAI API key&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: Create the Custom Module&lt;/h2&gt;
&lt;p&gt;Create a new folder:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        /modules/custom/ai_category/
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Inside it, create the below files:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;ai_category.info.yml&lt;/li&gt;
    &lt;li&gt;ai_category.module&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;&lt;i&gt;ai_category.info.yml&lt;/i&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;
    &lt;code&gt;
        name: AI Category Recommendation
        type: module
        description: Automatically recommend and assign taxonomy categories using AI.
        core_version_requirement: ^11
        package: Custom
        version: 1.0.0
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 2: Hook Into Node Save&lt;/h2&gt;
&lt;p&gt;We’ll use hook_entity_presave() to analyze content before it’s stored.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;i&gt;ai_category.module&lt;/i&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;
    &lt;code&gt;
        use Drupal\Core\Entity\EntityInterface;
        use Drupal\taxonomy\Entity\Term;

        /**
        * Implements hook_entity_presave().
        */
        function ai_category_entity_presave(EntityInterface $entity) {
            if ($entity-&gt;getEntityTypeId() !== &#39;node&#39;) {
                return;
            }

            // Only apply to articles (adjust as needed)
            if ($entity-&gt;bundle() !== &#39;article&#39;) {
                return;
            }

            $body = $entity-&gt;get(&#39;body&#39;)-&gt;value ?? &#39;&#39;;
            if (empty($body)) {
                return;
            }

            $category = ai_category_recommend_term($body);
            if ($category) {
                $entity-&gt;set(&#39;field_category&#39;, [&#39;target_id&#39; =&gt; $category]);
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This ensures our logic runs only for specific content types and avoids unnecessary processing.&lt;/p&gt;
&lt;h2&gt;Step 3: Ask AI for Category Recommendation&lt;/h2&gt;
&lt;p&gt;We’ll send the node content plus a list of available categories to OpenAI and ask it to pick the best one.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        function ai_category_recommend_term(string $text): ?int {
            $apiKey = &#39;YOUR_OPENAI_API_KEY&#39;;
            $endpoint = &#39;https://api.openai.com/v1/chat/completions&#39;;

            $terms = \Drupal::entityTypeManager()
                -&gt;getStorage(&#39;taxonomy_term&#39;)
                -&gt;loadTree(&#39;categories&#39;);

            $categoryNames = array_map(fn($t) =&gt; $t-&gt;name, $terms);

            $prompt = &quot;Choose the best category from this list:\n&quot;
                    . implode(&#39;, &#39;, $categoryNames)
                    . &quot;\n\nContent:\n&quot;
                    . strip_tags($text)
                    . &quot;\n\nReturn only the category name.&quot;;

            $payload = [
                &quot;model&quot; =&gt; &quot;gpt-4o-mini&quot;,
                &quot;messages&quot; =&gt; [
                [&quot;role&quot; =&gt; &quot;system&quot;, &quot;content&quot; =&gt; &quot;You are a content classification assistant.&quot;],
                [&quot;role&quot; =&gt; &quot;user&quot;, &quot;content&quot; =&gt; $prompt]
                ],
                &quot;temperature&quot; =&gt; 0
            ];

            $ch = curl_init($endpoint);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER =&gt; true,
                CURLOPT_HTTPHEADER =&gt; [
                &quot;Content-Type: application/json&quot;,
                &quot;Authorization: Bearer {$apiKey}&quot;
                ],
                CURLOPT_POST =&gt; true,
                CURLOPT_POSTFIELDS =&gt; json_encode($payload),
                CURLOPT_TIMEOUT =&gt; 15
            ]);

            $response = curl_exec($ch);
            curl_close($ch);

            $data = json_decode($response, true);
            $chosen = trim($data[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;] ?? &#39;&#39;);

            foreach ($terms as $term) {
                if (strcasecmp($term-&gt;name, $chosen) === 0) {
                return $term-&gt;tid;
                }
            }

            return null;
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h3&gt;What’s happening here:&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;Drupal loads all available categories&lt;/li&gt;
    &lt;li&gt;AI receives both content + allowed categories&lt;/li&gt;
    &lt;li&gt;AI returns one matching category name&lt;/li&gt;
    &lt;li&gt;Drupal maps it back to a taxonomy term ID&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 4: Enable the Module&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Place the module in /modules/custom/ai_category&lt;/li&gt;
    &lt;li&gt;Go to Extend → Enable module&lt;/li&gt;
    &lt;li&gt;Enable AI Category Recommendation&lt;/li&gt;
    &lt;li&gt;That’s it — no UI needed yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 5: Test It&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Create a new Article&lt;/li&gt;
    &lt;li&gt;Write content related to PHP, Drupal, AI, or CMS topics&lt;/li&gt;
    &lt;li&gt;Click Save&lt;/li&gt;
    &lt;li&gt;The Category field is auto-filled&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example:&lt;/h3&gt;
&lt;p&gt;Article content:&lt;/p&gt;
&lt;code&gt;
    &lt;pre&gt;
        “This tutorial explains how to build a custom Drupal 11 module using PHP hooks…”
    &lt;/pre&gt;
&lt;/code&gt;
&lt;p&gt;&lt;strong&gt;AI-selected category:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        Drupal
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Optional Enhancements&lt;/h2&gt;
&lt;p&gt;Once the basics work, you can extend this system:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Show AI recommendation as a suggestion, not auto-assignment&lt;/li&gt;
    &lt;li&gt;Add admin settings (API key, confidence threshold)&lt;/li&gt;
    &lt;li&gt;Use Queue API for bulk classification&lt;/li&gt;
    &lt;li&gt;Switch to embeddings for higher accuracy&lt;/li&gt;
    &lt;li&gt;Log category confidence scores&lt;/li&gt;
    &lt;li&gt;Support multi-term assignment&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Security &amp; Performance Tips&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Never hard-code API keys (use settings.php or environment variables)&lt;/li&gt;
    &lt;li&gt;Limit text length before sending to AI&lt;/li&gt;
    &lt;li&gt;Cache recommendations to reduce API calls&lt;/li&gt;
    &lt;li&gt;Add fallbacks if the AI response is invalid&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/12/ai-category-recommendation-system-for-drupal-11.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-7367602879157981117</guid><pubDate>Tue, 09 Dec 2025 13:21:00 +0000</pubDate><atom:updated>2026-03-18T13:18:30.754-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><title>AI Auto-Tagging in Laravel Using OpenAI Embeddings + Cron Jobs</title><description>&lt;p&gt;Manually tagging blog posts works fine when you have ten articles. At a hundred, it gets inconsistent. At a thousand, it&#39;s basically broken. Tags get applied differently depending on who wrote the post, and over time your taxonomy becomes a mess that&#39;s hard to search and harder to maintain.&lt;/p&gt;
&lt;p&gt;I wanted a way to fix this without retagging everything by hand. The approach I landed on uses OpenAI embeddings to represent both post content and tag names as vectors, then assigns tags based on how closely they match in meaning. &lt;/p&gt;
&lt;p&gt;The whole thing runs as a Laravel queue job triggered by a cron, so new posts get tagged automatically without any manual step.&lt;/p&gt;
&lt;p&gt;In this tutorial I&#39;ll walk you through the full setup: generating tag vectors, storing post embeddings, running the cosine similarity match, and wiring it all together with Laravel&#39;s scheduler.&lt;/p&gt;
&lt;h2&gt;What We&#39;re Constructing&lt;/h2&gt;
&lt;p&gt;You&#39;ll construct:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Table of Tag Vector&lt;/strong&gt; - The meaning of each tag (such as &quot;PHP&quot;, &quot;Laravel&quot;, &quot;Security&quot;, and &quot;AI&quot;) will be represented by an embedding vector created by AI.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;A Generator for Post Embedding&lt;/strong&gt; - We generate an embedding for the post content whenever a new post is saved.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;A Matching Algorithm&lt;/strong&gt; - The system determines which post embeddings are closest by comparing them with tag embeddings.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;A Cron Job&lt;/strong&gt; -The system automatically assigns AI-recommended tags every hour (or on any schedule). &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is ideal for:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Custom blogs made with Laravel&lt;/li&gt;
    &lt;li&gt;Headless CMS configurations&lt;/li&gt;
    &lt;li&gt;Tagging categories in e-commerce&lt;/li&gt;
    &lt;li&gt;Auto-classification of knowledge bases&lt;/li&gt;
    &lt;li&gt;Websites for documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let&#39;s get started.&lt;/p&gt;
&lt;h2&gt;Step 1: Create Migration for Tag Embeddings&lt;/h2&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:migration create_tag_embeddings_table&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Migration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
    public function up()
    {
        Schema::create(&#39;tag_embeddings&#39;, function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;unsignedBigInteger(&#39;tag_id&#39;)-&gt;unique();
            $table-&gt;json(&#39;embedding&#39;); // store vector
            $table-&gt;timestamps();
        });
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan migrate&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: Generate Embeddings for Tags&lt;/h2&gt;
&lt;p&gt;Create a command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:command GenerateTagEmbeddings&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add logic:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        public function handle()
        {
            $tags = Tag::all();

            foreach ($tags as $tag) {
                $vector = $this-&gt;embed($tag-&gt;name);

                TagEmbedding::updateOrCreate(
                    [&#39;tag_id&#39; =&gt; $tag-&gt;id],
                    [&#39;embedding&#39; =&gt; json_encode($vector)]
                );

                $this-&gt;info(&quot;Embedding created for tag: {$tag-&gt;name}&quot;);
            }
        }

        private function embed($text)
        {
            $client = new \GuzzleHttp\Client();

            $response = $client-&gt;post(&quot;https://api.openai.com/v1/embeddings&quot;, [
                &quot;headers&quot; =&gt; [
                    &quot;Authorization&quot; =&gt; &quot;Bearer &quot; . env(&#39;OPENAI_API_KEY&#39;),
                    &quot;Content-Type&quot; =&gt; &quot;application/json&quot;,
                ],
                &quot;json&quot; =&gt; [
                    &quot;model&quot; =&gt; &quot;text-embedding-3-large&quot;,
                    &quot;input&quot; =&gt; $text
                ]
            ]);

            $data = json_decode($response-&gt;getBody(), true);

            return $data[&#39;data&#39;][0][&#39;embedding&#39;] ?? [];
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Run once:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan generate:tag-embeddings&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now all tags have AI meaning vectors.&lt;/p&gt;
&lt;h2&gt;Step 3: Save Embeddings for Each Post&lt;/h2&gt;
&lt;p&gt;Add to your Post model observer or event.&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        $post-&gt;embedding = $this-&gt;embed($post-&gt;content);
        $post-&gt;save();
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Migration for posts:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        $table-&gt;json(&#39;embedding&#39;)-&gt;nullable();
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 4: Matching Algorithm (Post → Tags)&lt;/h2&gt;
&lt;p&gt;Create a helper class:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        class EmbeddingHelper
        {
            public static function cosineSimilarity($a, $b)
            {
                $dot = array_sum(array_map(fn($i, $j) =&gt; $i * $j, $a, $b));
                $magnitudeA = sqrt(array_sum(array_map(fn($i) =&gt; $i * $i, $a)));
                $magnitudeB = sqrt(array_sum(array_map(fn($i) =&gt; $i * $i, $b)));
                return $dot / ($magnitudeA * $magnitudeB);
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 5: Assign Tags Automatically (Queue Job)&lt;/h2&gt;
&lt;p&gt;Create job:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:job AutoTagPost&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Job logic:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        public function handle()
        {
            $postEmbedding = json_decode($this-&gt;post-&gt;embedding, true);

            $tags = TagEmbedding::with(&#39;tag&#39;)-&gt;get();

            $scores = [];
            foreach ($tags as $te) {
                $sim = EmbeddingHelper::cosineSimilarity(
                    $postEmbedding,
                    json_decode($te-&gt;embedding, true)
                );
                $scores[$te-&gt;tag-&gt;id] = $sim;
            }

            arsort($scores); // highest similarity first

            $best = array_slice($scores, 0, 5, true); // top 5 matches

            $this-&gt;post-&gt;tags()-&gt;sync(array_keys($best));
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Step 6: Cron Job to Process New Posts&lt;/h2&gt;
&lt;p&gt;Add to &lt;strong&gt;&lt;i&gt;app/Console/Kernel.php&lt;/i&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        protected function schedule(Schedule $schedule)
        {
            $schedule-&gt;command(&#39;ai:autotag-posts&#39;)-&gt;hourly();
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Create command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan make:command AutoTagPosts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Command logic:&lt;/p&gt;
&lt;pre&gt;
    &lt;code&gt;
        public function handle()
        {
            $posts = Post::whereNull(&#39;tags_assigned_at&#39;)-&gt;get();

            foreach ($posts as $post) {
                AutoTagPost::dispatch($post);
                $post-&gt;update([&#39;tags_assigned_at&#39; =&gt; now()]);
            }
        }
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Now, every hour, Laravel processes all new posts and assigns AI-selected tags.&lt;/p&gt;
&lt;h2&gt;Step 7: Test the Full Flow&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Create tags in admin&lt;/li&gt;
    &lt;li&gt;Run: php artisan generate:tag-embeddings&lt;/li&gt;
    &lt;li&gt;Create a new blog post&lt;/li&gt;
    &lt;li&gt;Cron or queue runs&lt;/li&gt;
    &lt;li&gt;Post automatically gets AI-selected tags&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Useful enhancements&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Weight tags by frequency&lt;/li&gt;
    &lt;li&gt;Use title + excerpt, not full content&lt;/li&gt;
    &lt;li&gt;Add confidence scores to DB&lt;/li&gt;
    &lt;li&gt;Auto-create new tags using AI&lt;/li&gt;
    &lt;li&gt;Add a manual override UI&lt;/li&gt;
    &lt;li&gt;Cache embeddings for performance&lt;/li&gt;
    &lt;li&gt;Batch process 1,000+ posts&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/12/ai-auto-tagging-in-laravel-using-openai.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-4637313492121432241</guid><pubDate>Tue, 18 Nov 2025 11:15:00 +0000</pubDate><atom:updated>2026-03-18T13:22:58.802-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Magento</category><title>Building an AI-Powered Product Description Generator in Magento 2 Using PHP &amp; OpenAI</title><description>&lt;p&gt;I was helping a client clean up their Magento store last month and they had over 400 products with either no description or a copy-pasted manufacturer blurb that was identical across 30 items. Writing them manually was not happening.&lt;/p&gt;
&lt;p&gt;So I threw together a quick module that puts a button on the product edit page. You click it, it grabs whatever attributes are already filled in and sends them to OpenAI, and a few seconds later the description fields are populated.&lt;/p&gt;
&lt;p&gt;Not perfect every time, but good enough as a starting point that you just edit rather than write from scratch.&lt;/p&gt;
&lt;p&gt;This tutorial shows you how I built it. The module itself is pretty lightweight, maybe 6 files total, and it works on Magento 2.4 with PHP 8.1.&lt;/p&gt;
&lt;h2&gt;What we are going to build&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;A button in Magento 2 for the admin that says &quot;Generate AI Description&quot;&lt;/li&gt;
    &lt;li&gt;An AJAX controller that sends product attributes to OpenAI&lt;/li&gt;
    &lt;li&gt;A description, short description, and meta content made by AI&lt;/li&gt;
    &lt;li&gt;Automatic insertion into Magento product fields&lt;/li&gt;
    &lt;li&gt;Optional: button to regenerate to get better results&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Magento 2.4+&lt;/li&gt;
    &lt;li&gt;PHP 8.1+&lt;/li&gt;
    &lt;li&gt;Composer&lt;/li&gt;
    &lt;li&gt;An OpenAI API key&lt;/li&gt;
    &lt;li&gt;Basic module development skills&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: Create a Magento Module Skeleton&lt;/h2&gt;
&lt;p&gt;Create your module folders:&lt;/p&gt;
&lt;pre&gt;
    app/code/AlbertAI/ProductDescription/
&lt;/pre&gt;
&lt;p&gt;Inside it, create &lt;strong&gt;&lt;i&gt;registration.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    use Magento\Framework\Component\ComponentRegistrar;

    ComponentRegistrar::register(
        ComponentRegistrar::MODULE,
        &#39;AlbertAI_ProductDescription&#39;,
        __DIR__
    );
&lt;/pre&gt;
&lt;p&gt;Then create &lt;strong&gt;&lt;i&gt;etc/module.xml&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
    &amp;lt;config xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
            xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:Module/etc/module.xsd&quot;&amp;gt;
        &amp;lt;module name=&quot;AlbertAI_ProductDescription&quot; setup_version=&quot;1.0.0&quot;/&amp;gt;
    &amp;lt;/config&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Enable the module:&lt;/p&gt;
&lt;pre&gt;
    php bin/magento setup:upgrade
&lt;/pre&gt;
&lt;h2&gt;Step 2: On the Product Edit Page, add a button that says &quot;Generate Description.&quot;&lt;/h2&gt;
&lt;p&gt;Create a file: &lt;strong&gt;&lt;i&gt;view/adminhtml/layout/catalog_product_edit.xml&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
    &amp;lt;page xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
        xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:View/Layout/etc/page_configuration.xsd&quot;&amp;gt;

        &amp;lt;body&amp;gt;
            &amp;lt;referenceBlock name=&quot;product_form&quot;&amp;gt;
                &amp;lt;block class=&quot;AlbertAI\ProductDescription\Block\Adminhtml\GenerateButton&quot;
                    name=&quot;ai_description_button&quot;/&amp;gt;
            &amp;lt;/referenceBlock&amp;gt;
        &amp;lt;/body&amp;gt;
    &amp;lt;/page&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Step 3: Create the Admin Button Block&lt;/h2&gt;
&lt;p&gt;File: &lt;strong&gt;&lt;i&gt;Block/Adminhtml/GenerateButton.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    namespace AlbertAI\ProductDescription\Block\Adminhtml;

    use Magento\Backend\Block\Template;

    class GenerateButton extends Template
    {
        protected $_template = &#39;AlbertAI_ProductDescription::button.phtml&#39;;
    }
&lt;/pre&gt;
&lt;h2&gt;Step 4: The Button Markup&lt;/h2&gt;
&lt;p&gt;File: &lt;strong&gt;&lt;i&gt;view/adminhtml/templates/button.phtml&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;button id=&quot;ai-generate-btn&quot; class=&quot;action-default scalable primary&quot;&amp;gt;
        Generate AI Description
    &amp;lt;/button&amp;gt;

    &amp;lt;script&amp;gt;
    require([&#39;jquery&#39;], function ($) {
        $(&#39;#ai-generate-btn&#39;).click(function () {
            const productId = $(&#39;#product_id&#39;).val();

            $.ajax({
                url: &#39;&lt;?= $block-&gt;getUrl(&quot;ai/generator/description&quot;) ?&gt;&#39;,
                type: &#39;POST&#39;,
                data: { product_id: productId },
                success: function (res) {
                    if (res.success) {
                        $(&#39;#description&#39;).val(res.description);
                        $(&#39;#short_description&#39;).val(res.short_description);
                        $(&#39;#meta_description&#39;).val(res.meta_description);
                        alert(&quot;AI description ready!&quot;);
                    } else {
                        alert(&quot;Error: &quot; + res.error);
                    }
                }
            });
        });
    });
    &amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Step 5: Create an Admin Route&lt;/h2&gt;
&lt;p&gt;File: &lt;strong&gt;&lt;i&gt;etc/adminhtml/routes.xml&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
    &amp;lt;config xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
            xsi:noNamespaceSchemaLocation=&quot;urn:magento:framework:App/etc/routes.xsd&quot;&amp;gt;

        &amp;lt;router id=&quot;admin&quot;&amp;gt;
            &amp;lt;route id=&quot;ai&quot; frontName=&quot;ai&quot;&amp;gt;
                &amp;lt;module name=&quot;AlbertAI_ProductDescription&quot;/&amp;gt;
            &amp;lt;/route&amp;gt;
        &amp;lt;/router&amp;gt;
    &amp;lt;/config&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Step 6: Build the AI Controller That Calls OpenAI&lt;/h2&gt;
&lt;p&gt;File: &lt;strong&gt;&lt;i&gt;Controller/Adminhtml/Generator/Description.php&lt;/i&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;
    namespace AlbertAI\ProductDescription\Controller\Adminhtml\Generator;

    use Magento\Backend\App\Action;
    use Magento\Catalog\Api\ProductRepositoryInterface;
    use Magento\Framework\Controller\Result\JsonFactory;

    class Description extends Action
    {
        protected $jsonFactory;
        protected $productRepo;
        private $apiKey = &quot;YOUR_OPENAI_API_KEY&quot;;

        public function __construct(
            Action\Context $context,
            ProductRepositoryInterface $productRepo,
            JsonFactory $jsonFactory
        ) {
            parent::__construct($context);
            $this-&gt;productRepo = $productRepo;
            $this-&gt;jsonFactory = $jsonFactory;
        }

        public function execute()
        {
            $result = $this-&gt;jsonFactory-&gt;create();

            $id = $this-&gt;getRequest()-&gt;getParam(&#39;product_id&#39;);
            if (!$id) {
                return $result-&gt;setData([&#39;success&#39; =&gt; false, &#39;error&#39; =&gt; &#39;Product not found&#39;]);
            }

            $product = $this-&gt;productRepo-&gt;getById($id);

            $prompt = sprintf(
                &quot;Write an SEO-friendly product description.\nProduct Name: %s\nBrand: %s\nFeatures: %s\nOutput: Long description, short description, and meta description.&quot;,
                $product-&gt;getName(),
                $product-&gt;getAttributeText(&#39;manufacturer&#39;),
                implode(&#39;, &#39;, $product-&gt;getAttributes())
            );

            $generated = $this-&gt;generateText($prompt);

            return $result-&gt;setData([
                &#39;success&#39; =&gt; true,
                &#39;description&#39; =&gt; $generated[&#39;long&#39;],
                &#39;short_description&#39; =&gt; $generated[&#39;short&#39;],
                &#39;meta_description&#39; =&gt; $generated[&#39;meta&#39;]
            ]);
        }

        private function generateText($prompt)
        {
            $body = [
                &quot;model&quot; =&gt; &quot;gpt-4.1-mini&quot;,
                &quot;messages&quot; =&gt; [
                    [&quot;role&quot; =&gt; &quot;user&quot;, &quot;content&quot; =&gt; $prompt]
                ]
            ];

            $ch = curl_init(&quot;https://api.openai.com/v1/chat/completions&quot;);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER =&gt; true,
                CURLOPT_HTTPHEADER =&gt; [
                    &quot;Content-Type: application/json&quot;,
                    &quot;Authorization: Bearer &quot; . $this-&gt;apiKey
                ],
                CURLOPT_POST =&gt; true,
                CURLOPT_POSTFIELDS =&gt; json_encode($body)
            ]);
            $response = json_decode(curl_exec($ch), true);
            curl_close($ch);

            $text = $response[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;] ?? &quot;No response&quot;;

            // Split via sections
            return [
                &#39;long&#39; =&gt; $this-&gt;extract($text, &#39;Long&#39;),
                &#39;short&#39; =&gt; $this-&gt;extract($text, &#39;Short&#39;),
                &#39;meta&#39; =&gt; $this-&gt;extract($text, &#39;Meta&#39;)
            ];
        }

        private function extract($text, $type)
        {
            preg_match(&quot;/$type Description:\s*(.+)/i&quot;, $text, $m);
            return $m[1] ?? $text;
        }
    }
&lt;/pre&gt;
&lt;h2&gt;Step 7: Test It&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Go to Magento &lt;strong&gt;Admin → Catalog → Products&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;Edit any product&lt;/li&gt;
    &lt;li&gt;Click “Generate AI Description”&lt;/li&gt;
    &lt;li&gt;Descriptions fields will auto-fill in seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bonus Tips&lt;/h2&gt;
&lt;p&gt;You can extend the module to generate:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Product titles&lt;/li&gt;
    &lt;li&gt;Bullet points&lt;/li&gt;
    &lt;li&gt;FAQ sections&lt;/li&gt;
    &lt;li&gt;Meta keywords&lt;/li&gt;
    &lt;li&gt;Category descriptions&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/11/building-ai-powered-product-description-in-magento2-with-openai.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-2544165567894079349</guid><pubDate>Mon, 10 Nov 2025 14:46:00 +0000</pubDate><atom:updated>2026-03-18T13:26:57.487-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Symfony</category><title>AI-Powered Semantic Search in Symfony Using PHP and OpenAI Embeddings</title><description>&lt;p&gt;LIKE/MATCH queries have a hard ceiling. I&#39;ve seen Symfony projects where the client kept complaining that search &quot;doesn&#39;t work&quot; and the real issue was never the code, it was that users don&#39;t search the way you index. They type &quot;how to reset password&quot; and your database has an article titled &quot;Account Recovery Guide.&quot; Zero overlap, zero results.&lt;/p&gt;
&lt;p&gt;Switching to OpenAI embeddings fixes this at the architecture level. Instead of matching strings, you convert both the query and your content into vectors and measure how close they are in meaning.&lt;/p&gt;
&lt;p&gt;A 1536-dimension float array per article sounds heavy but in practice it&#39;s stored as JSON in a text column and the whole thing runs fine on a standard MySQL setup for sites with a few thousand articles.&lt;/p&gt;
&lt;p&gt;This tutorial wires it up in Symfony using a console command to generate embeddings and a controller endpoint to run the search. No external vector database needed to get started.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Symfony 6 or 7&lt;/li&gt;
  &lt;li&gt;PHP 8.1+&lt;/li&gt;
  &lt;li&gt;Composer&lt;/li&gt;
  &lt;li&gt;A MySQL or SQLite database&lt;/li&gt;
  &lt;li&gt;
    An &lt;a href=&quot;https://platform.openai.com/&quot; target=&quot;_blank&quot;&gt;OpenAI API key&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 1: Create a New Symfony Command&lt;/h2&gt;
&lt;p&gt;
  We’ll use a console command to generate embeddings for your existing content
  (articles, pages, etc.).
&lt;/p&gt;
&lt;p&gt;Inside your Symfony project, run:&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;php bin/console make:command app:generate-embeddings&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;This will create a new file in &lt;b&gt;src/Command/GenerateEmbeddingsCommand.php&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;Replace its contents with the following:&lt;/p&gt;
&lt;h3&gt;src/Command/GenerateEmbeddingsCommand.php&lt;/h3&gt;
&lt;pre&gt;
namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Article;

#[AsCommand(
    name: &#39;app:generate-embeddings&#39;,
    description: &#39;Generate AI embeddings for all articles&#39;
)]
class GenerateEmbeddingsCommand extends Command
{
    private $em;
    private $apiKey = &#39;YOUR_OPENAI_API_KEY&#39;;
    private $endpoint = &#39;https://api.openai.com/v1/embeddings&#39;;

    public function __construct(EntityManagerInterface $em)
    {
        $this-&gt;em = $em;
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $articles = $this-&gt;em-&gt;getRepository(Article::class)-&gt;findAll();
        foreach ($articles as $article) {
            $embedding = $this-&gt;getEmbedding($article-&gt;getContent());
            if ($embedding) {
                $article-&gt;setEmbedding(json_encode($embedding));
                $this-&gt;em-&gt;persist($article);
                $output-&gt;writeln(&quot;✅ Generated embedding for article ID {$article-&gt;getId()}&quot;);
            }
        }

        $this-&gt;em-&gt;flush();
        return Command::SUCCESS;
    }

    private function getEmbedding(string $text): ?array
    {
        $payload = [
            &#39;model&#39; =&gt; &#39;text-embedding-3-small&#39;,
            &#39;input&#39; =&gt; $text,
        ];

        $ch = curl_init($this-&gt;endpoint);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER =&gt; true,
            CURLOPT_HTTPHEADER =&gt; [
                &quot;Content-Type: application/json&quot;,
                &quot;Authorization: Bearer {$this-&gt;apiKey}&quot;
            ],
            CURLOPT_POST =&gt; true,
            CURLOPT_POSTFIELDS =&gt; json_encode($payload)
        ]);

        $response = curl_exec($ch);
        curl_close($ch);

        $data = json_decode($response, true);
        return $data[&#39;data&#39;][0][&#39;embedding&#39;] ?? null;
    }
}
&lt;/pre&gt;
&lt;p&gt;This command takes every article from the database, sends its content to OpenAI’s Embedding API, and saves the resulting vector in a database field.&lt;/p&gt;
&lt;h2&gt;Step 2: Update the Entity&lt;/h2&gt;
&lt;p&gt;Assume your entity is App\Entity\Article.&lt;/p&gt;
&lt;p&gt;We’ll add a new column called embedding to store the vector data.&lt;/p&gt;
&lt;h3&gt;src/Entity/Article.php&lt;/h3&gt;
&lt;pre&gt;
    #[ORM\Column(type: &#39;text&#39;, nullable: true)]
    private ?string $embedding = null;

    public function getEmbedding(): ?string
    {
        return $this-&gt;embedding;
    }

    public function setEmbedding(?string $embedding): self
    {
        $this-&gt;embedding = $embedding;
        return $this;
    }
&lt;/pre&gt;
&lt;p&gt;Then update your database:&lt;/p&gt;
&lt;pre&gt;
    php bin/console make:migration
    php bin/console doctrine:migrations:migrate
&lt;/pre&gt;
&lt;h2&gt;Step 3: Create a Search Endpoint&lt;/h2&gt;
&lt;p&gt;We&#39;ll now include a basic controller that takes a search query, turns it into an embedding, and determines which article is the most semantically similar.&lt;/p&gt;
&lt;h3&gt;
    src/Controller/SearchController.php
&lt;/h3&gt;
&lt;pre&gt;
    namespace App\Controller;

    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    use Doctrine\ORM\EntityManagerInterface;
    use App\Entity\Article;

    class SearchController extends AbstractController
    {
        private $apiKey = &#39;YOUR_OPENAI_API_KEY&#39;;
        private $endpoint = &#39;https://api.openai.com/v1/embeddings&#39;;

        #[Route(&#39;/search&#39;, name: &#39;ai_search&#39;)]
        public function search(Request $request, EntityManagerInterface $em): Response
        {
            $query = $request-&gt;query-&gt;get(&#39;q&#39;);
            if (!$query) {
                return $this-&gt;json([&#39;error&#39; =&gt; &#39;Please provide a search query&#39;]);
            }

            $queryVector = $this-&gt;getEmbedding($query);
            $articles = $em-&gt;getRepository(Article::class)-&gt;findAll();

            $results = [];
            foreach ($articles as $article) {
                if ($article-&gt;getEmbedding()) {
                    $score = $this-&gt;cosineSimilarity(
                        $queryVector,
                        json_decode($article-&gt;getEmbedding(), true)
                    );
                    $results[] = [
                        &#39;id&#39; =&gt; $article-&gt;getId(),
                        &#39;title&#39; =&gt; $article-&gt;getTitle(),
                        &#39;similarity&#39; =&gt; $score,
                    ];
                }
            }

            usort($results, fn($a, $b) =&gt; $b[&#39;similarity&#39;] &lt;=&gt; $a[&#39;similarity&#39;]);
            return $this-&gt;json(array_slice($results, 0, 5)); // top 5 results
        }

        private function getEmbedding(string $text): array
        {
            $payload = [
                &#39;model&#39; =&gt; &#39;text-embedding-3-small&#39;,
                &#39;input&#39; =&gt; $text,
            ];

            $ch = curl_init($this-&gt;endpoint);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER =&gt; true,
                CURLOPT_HTTPHEADER =&gt; [
                    &quot;Content-Type: application/json&quot;,
                    &quot;Authorization: Bearer {$this-&gt;apiKey}&quot;
                ],
                CURLOPT_POST =&gt; true,
                CURLOPT_POSTFIELDS =&gt; json_encode($payload)
            ]);

            $response = curl_exec($ch);
            curl_close($ch);

            $data = json_decode($response, true);
            return $data[&#39;data&#39;][0][&#39;embedding&#39;] ?? [];
        }

        private function cosineSimilarity(array $a, array $b): float
        {
            $dot = 0; $magA = 0; $magB = 0;
            for ($i = 0; $i &lt; count($a); $i++) {
                $dot += $a[$i] * $b[$i];
                $magA += $a[$i] ** 2;
                $magB += $b[$i] ** 2;
            }
            return $dot / (sqrt($magA) * sqrt($magB));
        }
    }
&lt;/pre&gt;
&lt;p&gt;Now, even if the articles don&#39;t contain the exact keywords, your &lt;b&gt;&lt;i&gt;/search?q=php framework tutorial&lt;/i&gt;&lt;/b&gt; endpoint will return those that are most semantically similar to the query.&lt;/p&gt;
&lt;h2&gt;Step 4: Try It Out&lt;/h2&gt;
&lt;p&gt;Run the below command.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;php bin/console app:generate-embeddings&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;This generates embeddings for all articles.&lt;/p&gt;
&lt;p&gt;Now visit the following URL.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;http://your-symfony-app.local/search?q=learn symfony mvc&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;The top five most pertinent articles will be listed in a JSON response, arranged by meaning rather than keyword.&lt;/p&gt;
&lt;h2&gt;Real-World Applications&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;A more intelligent search within a CMS or knowledge base&lt;/li&gt;
    &lt;li&gt;AI-supported matching of FAQs&lt;/li&gt;
    &lt;li&gt;Semantic suggestions (&quot;you might also like...&quot;&lt;/li&gt;
    &lt;li&gt;Clustering of topics or duplicates in admin panels&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Tips for Security and Performance&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Reuse and cache embeddings (avoid making repeated API calls for the same content).&lt;/li&gt;
    &lt;li&gt;Keep your API key in.env.local (OPENAI_API_KEY=your_key).&lt;/li&gt;
    &lt;li&gt;For better performance, think about using a vector database such as Pinecone, Weaviate, or Qdrant if you have thousands of records.&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/11/ai-powered-semantic-search-in-symfony.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-7152762766851400842</guid><pubDate>Fri, 07 Nov 2025 12:58:00 +0000</pubDate><atom:updated>2026-03-18T13:29:19.257-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>AI Text Summarization for Drupal 11 Using PHP and OpenAI API</title><description>&lt;p&gt;Drupal&#39;s body field has a built-in summary subfield that almost nobody fills in properly. On high-volume editorial sites I&#39;ve worked on, it&#39;s either blank, copy-pasted from the first paragraph, or written by someone who clearly didn&#39;t read the article. It shows up in teasers, RSS feeds, and meta descriptions, so bad summaries actually hurt.&lt;/p&gt;
&lt;p&gt;The fix is straightforward. Hook into hook_entity_presave, grab the body content, send it to OpenAI, write the result back into body-&gt;summary before the node hits the database. Editors never have to touch it, and the summaries are actually coherent.&lt;/p&gt;
&lt;p&gt;This is a single-file custom module. No services, no config forms, no dependencies beyond cURL. If you want to wire it up properly with Drupal&#39;s config system later you can, but this gets you running in under 20 minutes.&lt;/p&gt;
&lt;div&gt;
   &lt;h2 style=&quot;text-align: left;&quot;&gt;Prerequisites&lt;/h2&gt;
   &lt;div&gt;Before starting, make sure you have:&lt;/div&gt;
   &lt;div&gt;
      &lt;ul style=&quot;text-align: left;&quot;&gt;
         &lt;li&gt;Drupal 11&lt;/li&gt;
         &lt;li&gt;PHP 8.1 or higher&lt;/li&gt;
         &lt;li&gt;Composer&lt;/li&gt;
         &lt;li&gt;cURL enabled&lt;/li&gt;
         &lt;li&gt;An &lt;a href=&quot;https://platform.openai.com/&quot; target=&quot;_blank&quot;&gt;OpenAI API key&lt;/a&gt;&lt;/li&gt;
      &lt;/ul&gt;
   &lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Step 1: Create a Custom Module&lt;/h2&gt;
&lt;p&gt;Create a new module called ai_summary.&lt;/p&gt;
/modules/custom/ai_summary/
&lt;p&gt;Inside that folder, create two files:&lt;/p&gt;
&lt;ul&gt;
   &lt;li&gt;ai_summary.info.yml&lt;/li&gt;
   &lt;li&gt;ai_summary.module&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ai_summary.info.yml&lt;/h3&gt;
&lt;p&gt;Add the below code in the info.yml file.&lt;/p&gt;
&lt;pre&gt;
   name: AI Summary
   type: module
   description: Automatically generate summaries for Drupal nodes using OpenAI API.
   core_version_requirement: ^11
   package: Custom
   version: 1.0.0
&lt;/pre&gt;
&lt;h3&gt;ai_summary.module&lt;/h3&gt;
&lt;p&gt;This is where the logic lives.&lt;/p&gt;
&lt;p&gt;To run our code just before a node is saved we will use &lt;strong&gt;&lt;i&gt;hook_entity_presave&lt;/i&gt;&lt;/strong&gt; of Drupal.&lt;/p&gt;
&lt;pre&gt;
use Drupal\node\Entity\Node;
use Drupal\Core\Entity\EntityInterface;

/**
* Implements hook_entity_presave().
*/
function ai_summary_entity_presave(EntityInterface $entity) {
    if ($entity-&gt;getEntityTypeId() !== &#39;node&#39;) {
        return;
    }

    // Only summarize articles (you can change this as needed)
    if ($entity-&gt;bundle() !== &#39;article&#39;) {
        return;
    }

    $body = $entity-&gt;get(&#39;body&#39;)-&gt;value ?? &#39;&#39;;
    if (empty($body)) {
        return;
    }

    // Generate AI summary
    $summary = ai_summary_generate_summary($body);

    if ($summary) {
        // Save it in the summary field
        $entity-&gt;get(&#39;body&#39;)-&gt;summary = $summary;
    }
}

/**
* Generate summary using OpenAI API.
*/
function ai_summary_generate_summary($text) {
    $api_key = &#39;YOUR_OPENAI_API_KEY&#39;;
    $endpoint = &#39;https://api.openai.com/v1/chat/completions&#39;;

    $payload = [
        &quot;model&quot; =&gt; &quot;gpt-4o-mini&quot;,
        &quot;messages&quot; =&gt; [
        [&quot;role&quot; =&gt; &quot;system&quot;, &quot;content&quot; =&gt; &quot;Summarize the following text in 2-3 sentences. Keep it concise and human-readable.&quot;],
        [&quot;role&quot; =&gt; &quot;user&quot;, &quot;content&quot; =&gt; $text]
        ],
        &quot;temperature&quot; =&gt; 0.7
    ];

    $ch = curl_init($endpoint);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER =&gt; true,
        CURLOPT_HTTPHEADER =&gt; [
        &quot;Content-Type: application/json&quot;,
        &quot;Authorization: Bearer {$api_key}&quot;
        ],
        CURLOPT_POST =&gt; true,
        CURLOPT_POSTFIELDS =&gt; json_encode($payload),
        CURLOPT_TIMEOUT =&gt; 15
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    $data = json_decode($response, true);
    return trim($data[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;] ?? &#39;&#39;);
}
&lt;/pre&gt;
&lt;p&gt;This functionality performs three primary functions:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;Identifies article saving in Drupal.&lt;/li&gt;
    &lt;li&gt;Sends the content to OpenAI to be summarized.&lt;/li&gt;
    &lt;li&gt;The summary is stored in the &lt;strong&gt;body summary&lt;/strong&gt; field of the article.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 2: Enable the Module&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;Place the new module folder directly in &lt;strong&gt;/modules/custom/&lt;/strong&gt;.&lt;/li&gt;
    &lt;li&gt;In Drupal Admin panel, go to: &lt;strong&gt;Extend → Install new module (or Enable module)&lt;/strong&gt;.&lt;/li&gt;
    &lt;li&gt;Check &lt;strong&gt;AI Summary&lt;/strong&gt; and turn it on.&lt;/li&gt;
&lt;/ol&gt; 
&lt;h2&gt;Step 3: Test the AI Summary&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;Select &lt;strong&gt;Content -&gt; Add content -&gt; Article&lt;/strong&gt;.&lt;/li&gt;
    &lt;li&gt;Enter the long paragraph in the &lt;strong&gt;body&lt;/strong&gt; field.&lt;/li&gt;
    &lt;li&gt;Save the article.&lt;/li&gt;
    &lt;li&gt;On reloading the page, open it one more time — the summary field will be already filled automatically.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Example: &lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Input Body:&lt;/h3&gt;
&lt;blockquote&gt;Artificial Intelligence has been changing how developers build and deploy applications...&lt;/blockquote&gt;
&lt;h3&gt;Generated Summary:&lt;/h3&gt;
&lt;blockquote&gt;AI is reshaping software development by automating repetitive tasks and improving decision-making through data-driven insights.&lt;/blockquote&gt;
&lt;h2&gt;Step 4: Extend It Further&lt;/h2&gt;
&lt;p&gt;The following are some of the ideas that can be used to improve the module:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Add settings:&lt;/strong&gt; Add a form to enable the user to add the API key and the select the type of model.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Queue processing:&lt;/strong&gt; Queue processing Use the drugndrup queue API to process the existing content in batches.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Custom field storage:&lt;/strong&gt; Store summaries in object now: field_ai_summary.&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Views integration:&lt;/strong&gt; Show or hide articles in terms of length of summary or its presence.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Security &amp; Performance Tips&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Never hardcode your API key but keep it in the configuration or in the.env file of Drupal.&lt;/li&gt;
    &lt;li&gt;Shorten long text in order to send (OpenAI token limit = cost).&lt;/li&gt;
    &lt;li&gt;Gracefully manage API timeouts.&lt;/li&gt;
    &lt;li&gt;Watchdoging errors to log API.&lt;/li&gt;
&lt;/ul&gt;</description><link>http://www.phpcmsframework.com/2025/11/ai-text-summarization-for-drupal-11.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-6175558991949598673</guid><pubDate>Mon, 03 Nov 2025 14:48:00 +0000</pubDate><atom:updated>2026-03-18T13:31:54.952-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">Joomla</category><title>Building a Sentiment Analysis Plugin in Joomla Using PHP and OpenAI API</title><description>&lt;p&gt;Joomla&#39;s content plugin system is underused. Most developers reach for components when a simple content plugin hooked into &lt;strong&gt;onContentBeforeSave&lt;/strong&gt; would do the job in a fraction of the code.&lt;/p&gt;
&lt;p&gt;This tutorial is a good example of that.
The idea is simple: every time an article is saved, we send the text to OpenAI and get back one word, positive, negative, or neutral. &lt;/p&gt;
&lt;p&gt;That result gets appended to the meta keywords field and flashed as an admin message. Nothing fancy, but on a community site or news portal where editors are processing dozens of submissions a day, having that sentiment label right in the save workflow saves real time.&lt;/p&gt;
&lt;p&gt;Two files, no Composer, no service container. Just a manifest XML and a single PHP class extending CMSPlugin.&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;What You’ll Need&lt;/h3&gt;&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Joomla 5.x installed&lt;/li&gt;&lt;li&gt;PHP 8.1 or newer&lt;/li&gt;&lt;li&gt;cURL enabled on your server&lt;/li&gt;&lt;li&gt;An &lt;a href=&quot;https://platform.openai.com/docs/overview&quot; target=&quot;_blank&quot;&gt;OpenAI API key&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Once that’s ready, let’s code.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 1: Creation of the Plugin&lt;/h3&gt;&lt;p&gt;In your Joomala system, make a new folder within the system under the name of the plugin:&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/plugins/content/aisentiment/&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Thereupon in that folder generate two files:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;b&gt;aisentiment.php&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;aisentiment.xml&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;aisentiment.xml&lt;/h4&gt;&lt;p&gt;This is the manifest file that the Joomla plugin identifies the identity of this particular plugin and the files that should be loaded into it.&lt;/p&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;p&gt;&amp;lt;extension type=&quot;plugin&quot; version=&quot;5.0&quot; group=&quot;content&quot; method=&quot;upgrade&quot;&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;name&amp;gt;plg_content_aisentiment&amp;lt;/name&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;author&amp;gt;PHP CMS Framework&amp;lt;/author&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;description&amp;gt;Analyze sentiment of comments or articles using OpenAI API.&amp;lt;/description&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;files&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;filename plugin=&quot;aisentiment&quot;&amp;gt;aisentiment.php&amp;lt;/filename&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/files&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;/extension&amp;gt;&lt;/p&gt;&lt;/code&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 2: Add the PHP Logic&lt;/h3&gt;&lt;p&gt;Now let’s write the plugin code.&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;aisentiment.php&lt;/h4&gt;&lt;div&gt;&lt;div&gt;&lt;i&gt;&amp;lt;?php&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;defined(&#39;_JEXEC&#39;) or die;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;use Joomla\CMS\Plugin\CMSPlugin;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;use Joomla\CMS\Factory;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;class PlgContentAisentiment extends CMSPlugin&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;{&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; private $apiKey = &#39;YOUR_OPENAI_API_KEY&#39;;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; private $endpoint = &#39;https://api.openai.com/v1/chat/completions&#39;;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; public function onContentBeforeSave($context, $table, $isNew, $data)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; {&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Only process if content exists&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (empty($data[&#39;introtext&#39;]) &amp;amp;&amp;amp; empty($data[&#39;fulltext&#39;])) {&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return true;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $text = strip_tags($data[&#39;introtext&#39;] ?? $data[&#39;fulltext&#39;]);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $sentiment = $this-&amp;gt;getSentiment($text);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if ($sentiment) {&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $data[&#39;metakey&#39;] .= &#39; Sentiment:&#39; . ucfirst($sentiment);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Factory::getApplication()-&amp;gt;enqueueMessage(&quot;AI Sentiment: {$sentiment}&quot;, &#39;message&#39;);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return true;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; }&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; private function getSentiment($text)&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; {&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $payload = [&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;model&quot; =&amp;gt; &quot;gpt-4o-mini&quot;,&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;messages&quot; =&amp;gt; [&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [&quot;role&quot; =&amp;gt; &quot;system&quot;, &quot;content&quot; =&amp;gt; &quot;You are a sentiment analysis model. Return only one word: positive, negative, or neutral.&quot;],&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [&quot;role&quot; =&amp;gt; &quot;user&quot;, &quot;content&quot; =&amp;gt; $text]&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ]&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ];&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $ch = curl_init($this-&amp;gt;endpoint);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; curl_setopt_array($ch, [&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CURLOPT_RETURNTRANSFER =&amp;gt; true,&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CURLOPT_HTTPHEADER =&amp;gt; [&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;Content-Type: application/json&quot;,&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;Authorization: Bearer {$this-&amp;gt;apiKey}&quot;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ],&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CURLOPT_POST =&amp;gt; true,&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CURLOPT_POSTFIELDS =&amp;gt; json_encode($payload),&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; CURLOPT_TIMEOUT =&amp;gt; 15&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ]);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $response = curl_exec($ch);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; curl_close($ch);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $data = json_decode($response, true);&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return strtolower(trim($data[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;] ?? &#39;neutral&#39;));&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; }&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;}&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;This extension will be installed on Joomla and will work together with the content workflow (onContentBeforeSave) and send the content of the article to the OpenAI API. This model examines the tone and produces one of three values, including positive, negative, and neutral.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The output is presented in the administration of Joomla as an output when the article is saved.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 3:Install and activate the Plugin.&lt;/h3&gt;&lt;div&gt;Zip the folder (&lt;b&gt;aisentiment&lt;/b&gt;) of the compressor:&amp;nbsp;&lt;b&gt;aisentiment.zip&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Within Joomla administration log, go to: &lt;b&gt;System → Extensions → Install&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Upload the zip file.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once installed, visit &lt;b&gt;System -&amp;gt;&amp;nbsp;Plugins &lt;/b&gt;where you will find the &lt;b&gt;&lt;i&gt;AI Sentiment&lt;/i&gt;&lt;/b&gt; one and turn it on.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 4: Test It&lt;/h3&gt;&lt;div&gt;Open an existing article, or create one.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enter some text — for example:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;This product is out of my expectations and it works excellently!&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Click &lt;b&gt;Save&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Joomla will show such a message as:&amp;nbsp; &lt;b&gt;&lt;i&gt;AI Sentiment: positive&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Or you can save the response in a custom field and present it on the front end.&lt;/div&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;Bonus Tips:&lt;/h3&gt;&lt;div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Store your API key securely in Joomla’s configuration or an environment variable (not hard-coded).&lt;/li&gt;&lt;li&gt;Add caching if you’re analyzing large volumes of content.&lt;/li&gt;&lt;li&gt;Trim long text before sending to OpenAI to save API tokens.&lt;/li&gt;&lt;li&gt;Handle failed API calls gracefully with proper fallbacks.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Real-World Use Cases:&lt;/h3&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Highlight positive user reviews automatically.&lt;/li&gt;&lt;li&gt;Flag negative feedback for moderation.&lt;/li&gt;&lt;li&gt;Generate sentiment dashboards for community comments.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;In the next part of our AI + PHP CMS series, we’ll move to &lt;b&gt;Drupal 11&lt;/b&gt;, where we’ll build an &lt;b&gt;&lt;i&gt;&lt;a href=&quot;https://www.phpcmsframework.com/2025/11/ai-text-summarization-for-drupal-11.html&quot; target=&quot;_blank&quot;&gt;AI Text Summarization module using PHP and OpenAI API&lt;/a&gt;&lt;/i&gt;&lt;/b&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description><link>http://www.phpcmsframework.com/2025/11/joomla-sentiment-analysis-plugin.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-2738600443491998847</guid><pubDate>Sat, 29 Jul 2023 15:51:00 +0000</pubDate><atom:updated>2023-07-29T08:51:49.289-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>Unleashing the Power of Drupal 10: Advantages and Features</title><description>As technology continues to evolve, so does the web development landscape, and Drupal, the renowned open-source content management system (CMS), stays at the forefront of innovation. With the release of Drupal 10, developers and site owners have access to a host of exciting advantages and features that take web development to new heights. In this blog post, we&#39;ll explore the key advantages of Drupal 10 and highlight the cutting-edge features that make it the preferred choice for building powerful and scalable websites.
&lt;h2&gt;1. Enhanced Accessibility&lt;/h2&gt;
&lt;p&gt;Accessibility is no longer an option; it&#39;s a necessity. Drupal 10 understands this, and its commitment to making the web inclusive for all is evident through its enhanced accessibility features. The platform comes with improved semantic HTML, ARIA enhancements, and focus management, ensuring that websites built on Drupal 10 are accessible to people with disabilities. By prioritizing accessibility, you can reach a wider audience and demonstrate your commitment to providing a positive user experience for everyone.&lt;/p&gt;
&lt;h2&gt;2. API-First Approach&lt;/h2&gt;
&lt;p&gt;Drupal 10 embraces the API-first approach, a trend that&#39;s gaining momentum in the web development community. This feature allows developers to separate the frontend and backend of their websites, giving them the freedom to choose the best technology stack for each aspect. With Drupal&#39;s robust APIs, you can build headless or decoupled applications, providing seamless user experiences across various devices and platforms. This versatility is ideal for creating dynamic applications, progressive web apps (PWAs), and mobile experiences.&lt;/p&gt;
&lt;h2&gt;3. Streamlined Content Editing with Claro&lt;/h2&gt;
&lt;p&gt;Drupal 10 introduces the Claro administration theme, a significant improvement to the user interface for content editors and site administrators. Claro is designed to be visually appealing, intuitive, and user-friendly, making content management a breeze. With a modern and clean interface, content editors can focus on creating engaging content without getting bogged down by complicated backend systems. Claro streamlines the content editing process, increasing efficiency and productivity for website maintenance tasks.&lt;/p&gt;
&lt;h2&gt;4. Effortless Migration from Drupal 9&lt;/h2&gt;
&lt;p&gt;For organizations currently using Drupal 9, upgrading to Drupal 10 is a smooth and hassle-free process. The Drupal community has worked diligently to ensure backward compatibility between the versions, making it easier to migrate your existing projects seamlessly. This allows you to take advantage of Drupal 10&#39;s new features while leveraging your existing codebase and modules.&lt;/p&gt;
&lt;h2&gt;5. Improved Performance and Scalability&lt;/h2&gt;
&lt;p&gt;Performance is critical for any website, and Drupal 10 comes packed with performance improvements that enhance the overall speed and efficiency of your site. The platform has undergone codebase optimizations, caching enhancements, and database query optimizations, resulting in faster page load times and improved responsiveness. Whether you run a small blog or a large enterprise platform, Drupal 10 can handle the scale, ensuring a smooth user experience for all visitors.&lt;/p&gt;
&lt;h2&gt;6. Robust Security Features&lt;/h2&gt;
&lt;p&gt;Security is a paramount concern for any CMS, and Drupal has a long-standing reputation for its robust security architecture. Drupal 10 continues this tradition, providing a secure foundation for your web projects. Regular security updates, improved password hashing algorithms, and adherence to industry best practices make Drupal 10 a secure choice for building websites and applications.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Drupal 10 is a game-changer in the world of web development, offering a wide array of advantages and features that cater to the needs of modern websites and applications. With enhanced accessibility, an API-first approach, streamlined content editing, easy migration, improved performance, and robust security features, Drupal 10 empowers developers and site owners to build cutting-edge digital experiences.&lt;/p&gt;
&lt;p&gt;Whether you&#39;re starting a new project or upgrading an existing one, Drupal 10 provides the flexibility, scalability, and security required to create exceptional websites that engage and delight users. Embrace the power of Drupal 10 and elevate your web development endeavors to new heights. Happy building!&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2023/07/unleashing-power-of-drupal-10.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-952465707605712357</guid><pubDate>Thu, 27 Jul 2023 17:41:00 +0000</pubDate><atom:updated>2023-07-27T10:41:45.008-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>Introducing Drupal 10: The Next Evolution in Web Development</title><description>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Welcome to the future of web development! Drupal, the powerful open-source content management system (CMS), has reached a new milestone with the release of Drupal 10. As we bid adieu to Drupal 9 and embrace the latest iteration, developers and site builders can look forward to a plethora of exciting features, enhancements, and optimizations that make building robust, secure, and scalable websites easier than ever. In this blog post, we&#39;ll delve into the highlights of Drupal 10 and explore the reasons why it&#39;s the ultimate choice for your web projects.&lt;/p&gt;
&lt;h2&gt;1. Accessibility First&lt;/h2&gt;
&lt;p&gt;Accessibility has always been a priority for Drupal, and with Drupal 10, this commitment is taken to new heights. The core development team has worked tirelessly to ensure that Drupal 10 adheres to the latest web accessibility standards, making it possible for everyone to access and interact with websites built on the platform, regardless of their physical abilities. Improved semantic HTML, ARIA enhancements, and focus management are some of the accessibility-focused improvements that will empower developers to create inclusive digital experiences effortlessly.&lt;/p&gt;
&lt;h2&gt;2. API-First Approach&lt;/h2&gt;
&lt;p&gt;Drupal has been at the forefront of embracing API-first design principles, and this trend continues with Drupal 10. The new version comes with enhanced support for headless and decoupled architectures, allowing developers to use Drupal as a content repository while building frontend experiences using the technologies of their choice, such as React, Angular, or Vue.js. This flexibility opens up a world of possibilities for creating seamless omnichannel experiences and empowers organizations to deliver content to various devices and platforms.&lt;/p&gt;
&lt;h2&gt;3. Easy Migration from Drupal 9&lt;/h2&gt;
&lt;p&gt;If you&#39;re already using Drupal 9 for your website, transitioning to Drupal 10 is a breeze. The development team has streamlined the upgrade process, making it easier and faster to move your existing projects to the latest version. Additionally, Drupal 10&#39;s backward compatibility ensures that modules and themes written for Drupal 9 will continue to function smoothly, allowing you to leverage your existing codebase and investments.&lt;/p&gt;
&lt;h2&gt;4. Enhanced Performance and Scalability&lt;/h2&gt;
&lt;p&gt;Drupal 10 brings significant performance improvements, optimizing the CMS to handle larger and more complex websites with ease. Whether you&#39;re running a small business site or a high-traffic enterprise platform, Drupal 10&#39;s enhancements in caching, database queries, and overall codebase optimization will ensure your website performs at its best, providing a seamless user experience even during peak traffic.&lt;/p&gt;
&lt;h2&gt;5. Streamlined Content Editing&lt;/h2&gt;
&lt;p&gt;Content editors and site administrators will love the enhanced content editing experience in Drupal 10. With the introduction of the Claro administration theme in Drupal 8 and further improvements in Drupal 9, Drupal 10 takes the user interface to the next level. The admin interface is now more intuitive, user-friendly, and visually appealing, reducing the learning curve and making content management a joyous task.&lt;/p&gt;
&lt;h2&gt;6. Robust Security Features&lt;/h2&gt;
&lt;p&gt;Security remains a top priority for Drupal, and Drupal 10 continues to build upon the platform&#39;s already robust security architecture. With regular security updates and features like improved password hashing algorithms, security-conscious organizations can trust Drupal 10 to safeguard their digital assets effectively.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Drupal 10 marks a significant milestone in the evolution of the CMS, presenting an impressive array of features that cater to the needs of modern web development. From enhanced accessibility to API-first capabilities, easy migration, improved performance, streamlined content editing, and robust security features, Drupal 10 promises to revolutionize the way websites are built and managed.&lt;/p&gt;
&lt;p&gt;So, whether you&#39;re a developer, site builder, or organization looking to create dynamic and innovative digital experiences, Drupal 10 provides the ideal platform to bring your vision to life. Embrace the power of Drupal 10 and join the ever-growing community of web professionals shaping the future of the internet. Happy building!&lt;/p&gt;</description><link>http://www.phpcmsframework.com/2023/07/introducing-drupal-10-next-evolution-in.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-1496918759422080671</guid><pubDate>Mon, 14 Feb 2022 19:29:00 +0000</pubDate><atom:updated>2022-02-14T11:34:17.780-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Symfony</category><title>Steps to create a Contact Form in Symfony With SwiftMailer</title><description>&lt;p style=&quot;text-align: left;&quot;&gt;In this article, we are going to discuss about How we can create a contact form in Symfony with SwiftMailer. &lt;a href=&quot;http://www.phpcmsframework.com/2012/08/symfony-framework-introduction.html&quot;&gt;Symfony&lt;/a&gt; provides an architecture, components and tools for developers to build complex web applications faster. Choosing &lt;a href=&quot;http://www.phpcmsframework.com/2012/08/symfony-framework-introduction.html&quot;&gt;symfony&lt;/a&gt; allows you to release your applications earlier, host and scale them without problem, and maintain them over time with no surprise.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://swiftmailer.symfony.com/docs/introduction.html&quot;&gt;Swift Mailer&lt;/a&gt; is a component based library for sending e-mails from PHP applications. Swift Mailer supports PHP 7.0 to PHP 8.1 included (proc_* functions must be available). &lt;a href=&quot;https://swiftmailer.symfony.com/docs/introduction.html&quot;&gt;Swift Mailer&lt;/a&gt; does not work when used with function overloading as implemented by mbstring when mbstring.func_overload is set to 2.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;At the end of this article, we will be created a contact form in Symfony using Form Builder and connected with SwiftMailer bundle. So that, the visitor could receive acknowledgement that the messages has been successfully sent.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Step 1: Create Contact Entity with Doctrine&lt;/span&gt;&lt;/h3&gt;First of all we need to create and configure the database. Database related information and credentials are available in &lt;b&gt;Application Access details panel.&lt;/b&gt; Add these to the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;app/config/parameters.yml&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;pre class=&quot;lang:yaml decode:true&quot; style=&quot;background-color: whitesmoke; border-radius: 4px; border: 1px solid rgb(204, 204, 204); box-sizing: border-box; color: #333333; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; line-height: 1.42857; margin-bottom: 10px; margin-top: 0px; overflow-wrap: break-word; overflow: auto; padding: 9.5px; word-break: break-all;&quot; title=&quot;Parameters.yml&quot;&gt;parameters:

&amp;nbsp;&amp;nbsp;&amp;nbsp;database_host: localhost
&amp;nbsp;&amp;nbsp;&amp;nbsp;database_port: &amp;lt;PORT&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;database_name: &amp;lt;DB_NAME&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;database_user: &amp;lt;DB_user_name&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;database_password: &amp;lt;DB_password&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot;&gt; Next, we need to create a contact entity with Doctrine. To use Doctrine, open SSH terminal and go to your Symfony project:&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;cd application/&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;your_app_folder&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/public_html&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now run the following command to start the entity generator:&lt;/p&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;php bin/console doctrine:generate:entity&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;text-align: left;&quot;&gt;For the contact form I need four fields: &lt;b&gt;name&lt;/b&gt;, &lt;b&gt;email&lt;/b&gt;, &lt;b&gt;subject&lt;/b&gt;, &lt;b&gt;message&lt;/b&gt;. You can add more fields as per your requirements. The entity name will be Contact and the shortcut name will be &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;AppBundle:Contact&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;. Annotation will be &lt;b&gt;Yaml&lt;/b&gt;, so just press the &lt;b&gt;Enter &lt;/b&gt;key on this.&lt;/p&gt;&lt;p style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiOU_aBVPLaRYwtECoM-IMOAAxVKl-p-Ho2hrcNzgI1EOL0bp9fEwBrpM7E2g1skbtbLkTLYsuysyEWQ-esl5ZgY1AXnZ5MOm0bEK0M1l88_0hbJYSzlkUM-GLlINxncE_hpOwRVxSqavoDdJ7rD-rgUrD5HsGUqm8MUv5rW1qh14xMIxS8JeTkaASk=s667&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;600&quot; data-original-width=&quot;667&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiOU_aBVPLaRYwtECoM-IMOAAxVKl-p-Ho2hrcNzgI1EOL0bp9fEwBrpM7E2g1skbtbLkTLYsuysyEWQ-esl5ZgY1AXnZ5MOm0bEK0M1l88_0hbJYSzlkUM-GLlINxncE_hpOwRVxSqavoDdJ7rD-rgUrD5HsGUqm8MUv5rW1qh14xMIxS8JeTkaASk=w400-h360&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now go to &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;src/AppBundle/Entity&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;. You will see a &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Contact.php&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; file in which Doctrine has created a new method for every field. I will use these methods to set values in the Controller:&lt;/p&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;de1&quot; style=&quot;box-sizing: border-box; color: #cf6a4c; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; AppBundle\Entity;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Doctrine\&lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;ORM&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;\Mapping &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;ORM&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;* Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;*&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;* @ORM\Table(name=&quot;contact&quot;)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;* @ORM\Entity(repositoryClass=&quot;AppBundle\Repository\ContactRepository&quot;)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;*/&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @var int&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Column(name=&quot;id&quot;, type=&quot;integer&quot;)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Id&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\GeneratedValue(strategy=&quot;AUTO&quot;)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$id&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @var string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Column(name=&quot;name&quot;, type=&quot;string&quot;, length=255)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$name&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @var string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Column(name=&quot;email&quot;, type=&quot;string&quot;, length=255)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$email&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @var string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Column(name=&quot;subject&quot;, type=&quot;string&quot;, length=255)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$subject&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @var string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @ORM\Column(name=&quot;message&quot;, type=&quot;string&quot;, length=255)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Get id&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return int&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Set name&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @param string $name&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$name&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Get name&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Set email&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @param string $email&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setEmail&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$email&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Get email&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Set subject&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @param string $subject&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setSubject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$subject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;subject&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$subject&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Get subject&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getSubject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;subject&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Set message&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @param string $message&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return Contact&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setMessage&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * Get message&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    *&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @return string&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getMessage&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me1&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Next, we will work on the Controller and the form View.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Step 2: Create the Form in DefaultController.php&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The next step is to create a form in the controller. You can also create a form in the Twig view. However, in this article, I will initialize the form fields using the Symfony’s form builder, and then show the form Widget in the View.&lt;/p&gt;Open &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;DefaultController.php &lt;/span&gt;&lt;/i&gt;&lt;/b&gt;and add the following namesapaces and the entity (created earlier):&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; AppBundle\Controller;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Bundle\FrameworkBundle\Controller\Controller;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\HttpFoundation\Request;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\HttpFoundation\Response;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;For&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m\Extension\Core\Type\TextType;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;For&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m\Extension\Core\Type\TextareaType;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;For&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m\Extension\Core\Type\DateTimeType;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;For&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m\Extension\Core\Type\ChoiceType;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;For&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m\Extension\Core\Type\SubmitType;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Symfony\Component\HttpFoundation\Session\Flash\FlashBag;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; AppBundle\Entity\Contact;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now in the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;createAction() &lt;/span&gt;&lt;/i&gt;&lt;/b&gt;method, create an entity object and pass it to the form builder. The form contains the input fields (as discussed earlier).&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; DefaultController &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;extends&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Controller&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;/**&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    * @Route(&quot;/form&quot;, name=&quot;homepage&quot;)&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co2&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;    */&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;createAction&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;Request &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$contact&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; Contact;     &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;     # Add &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m fields&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$form&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw4&quot; style=&quot;box-sizing: border-box; color: #990073; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;createFormBuilder&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$contact&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, TextType::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;attr&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;form-control&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;margin-bottom:15px&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;email&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, TextType::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;email&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;attr&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;form-control&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;margin-bottom:15px&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;subject&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, TextType::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;subject&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;attr&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;form-control&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;margin-bottom:15px&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, TextareaType::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;attr&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;form-control&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;Save&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, SubmitType::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;label&#39;&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;submit&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;attr&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;class&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;btn btn-primary&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st1&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;margin-top:15px&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getForm&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;     # Handle &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;m response&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$form&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sy0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;me0&quot; style=&quot;box-sizing: border-box; color: #0086b3; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kw3&quot; style=&quot;box-sizing: border-box; color: #445588; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Step 3: Create View for the Contact Form&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;To view this form on the Twig template, create a file &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;form.html.twig&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; in &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;app/Resources/views/default&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; and add the form widget to it.&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% block body %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;div class=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;div class=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;row&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           &amp;lt;div class=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;col-sm-4&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;               &amp;lt;h2 class=page-header&amp;gt;Contact Form in Symfony&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form_start&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form_widget&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;                   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form_end&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endblock %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;I have added bootstrap classes to the code. I will now add the bootstrap CDN to base template to make the classes work.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Open the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;base.html.twig&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; from &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;app/Resources/views&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; and add the CDN links to it.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;head&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;meta charset=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;title&amp;gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% block title %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;Welcome!&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endblock %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% block stylesheets %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endblock %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;link rel=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;icon&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; type=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;image/x-icon&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; href=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;{{ asset(&#39;favicon.ico&#39;) }}&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;script src=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &amp;lt;link href=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; rel=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;body&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;div class=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;alert alert-success&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% set flashbag_notices = app.session.flashbag.&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;notice&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% if flashbag_notices is not empty %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;div class=&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;flash-notice&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% for notice in flashbag_notices %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endfor %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endif %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% block body %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endblock %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% block javascripts %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% endblock %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   &amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now I need to extend &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;form.html.twig&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; with &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;base.html.twig &lt;/span&gt;&lt;/b&gt;&lt;/i&gt;by simply adding the following line at the top.&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 5px 14px;&quot;&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;% extends &lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;base.html.twig&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; %&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;At this point, if you hit the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/form&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; Route, you will get the following Form:&lt;/p&gt;&lt;p style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj5ciw48lLPQ0SNW1roh8hwYpgB2jJ4RQpajpD52YgM5FrRnqdOJm5YCed82tKWySttCSZ6bsHWIqzh7MbpJj6JPK4f51FmQGNNyXy1BueXqOLSX5oTTiY_KJZvdmlMwZlB8udrWYKo8hDHeaA2_awEBKZfUae_Ay8kFPrzwWGp-xgxryjbzlc7CUQh=s511&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;511&quot; data-original-width=&quot;506&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEj5ciw48lLPQ0SNW1roh8hwYpgB2jJ4RQpajpD52YgM5FrRnqdOJm5YCed82tKWySttCSZ6bsHWIqzh7MbpJj6JPK4f51FmQGNNyXy1BueXqOLSX5oTTiY_KJZvdmlMwZlB8udrWYKo8hDHeaA2_awEBKZfUae_Ay8kFPrzwWGp-xgxryjbzlc7CUQh=w396-h400&quot; width=&quot;396&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Step 4: Save Values in the Database&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Next, we will save the values in database. We have already created the form in the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;DefaultController&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; class. First, we will save the values in variables and then pass these variables to the related methods in the entity. Finally, we will save the variables through the &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;persist()&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; method.&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co1&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;# check if form is submitted &lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;       &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$form-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;isSubmitted&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; &amp;amp;&amp;amp;  $form-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $name = $form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $email = $form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;email&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $subject = $form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;subject&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $message = $form&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getData&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;; &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;co1&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;# set form data   &lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $contact-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $contact-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setEmail&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;          &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $contact-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setSubject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$subject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;     &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $contact-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setMessage&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;                &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;co1&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;# finally add data in database&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $sn = $this-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getDoctrine&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;getManager&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;      &lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $sn -&amp;gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$contact&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;           $sn -&amp;gt; &lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now, when the form is submitted, the values will be available in the database.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;Step 5: Send Acknowledgement to the User&lt;/span&gt;&lt;/h3&gt;&lt;p style=&quot;text-align: left;&quot;&gt;It is important to tell the user that their message has been successfully delivered to the website. Many websites send an email that provides all user-submitted data in a proper format.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;For this purpose, I will use &lt;b&gt;SwiftMailer&lt;/b&gt;, a bundle that comes preinstalled in &lt;b&gt;&lt;a href=&quot;http://www.phpcmsframework.com/2012/08/symfony-framework-introduction.html&quot;&gt;Symfony 3&lt;/a&gt;&lt;/b&gt;. To use it, you just need to configure it. To do the necessary changes, open &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;app/config/parameters.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; under &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;parameters&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   mailer_transport: gmail&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   mailer_host: smtp.gmail.com&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   mailer_user: &amp;lt;SMTP_USERNAME&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   mailer_password: &amp;lt;SMTP_PASSWORD&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   secret: Itmaybeanything&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;   encryption: tls&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now in the &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;config.yml&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;, add the following code to get the values:&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;co1&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;# Swiftmailer Configuration&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;swiftmailer:&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      transport: &lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;%mailer_transport%&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      host:      &lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;%mailer_host%&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      username:  &lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;%mailer_user%&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      password:  &lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&quot;%mailer_password%&quot;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;      spool:     &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; type: memory &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;The configuration process is over. Now, open &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;DefaultController.php&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; and add the code for SwiftMailer. I will use the same data variables in this code:&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message = \Swift_Message::&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;               -&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setSubject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$subject&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;               -&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setFrom&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;shahroznawaz156@gmail.com&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;               -&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setTo&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;               -&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;setBody&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;renderView&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;default/sendemail.html.twig&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; =&amp;gt; $name&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;text/html&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$this-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st0&quot; style=&quot;box-sizing: border-box; color: #dd1144; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&#39;mailer&#39;&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;kw1&quot; style=&quot;box-sizing: border-box; color: #286491; font-family: inherit; font-size: 12px; font-weight: 700; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;$message&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Notice that you can also send email templates. In this code snippet, I sent the email template I created (&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;sendemail.html.twig&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;) in the &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;views/default&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;. This template has a simple test message.&lt;/p&gt;&lt;div&gt;&lt;ol class=&quot;hoverEnabled enlighterEnlighterJS EnlighterJS&quot; style=&quot;background-color: #f9f9f9; border-radius: 8px; border: 1px solid rgb(224, 224, 224); box-sizing: border-box; color: #939393; font-family: &amp;quot;Source Code Pro&amp;quot;, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 11px; line-height: 26px; list-style: none; margin: 0px 0px 20px; overflow-wrap: break-word; overflow: auto; padding: 0px; white-space: pre-wrap;&quot;&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: #fffcd3; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #444444; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 5px 5px 1px 14px;&quot;&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;co1&quot; style=&quot;box-sizing: border-box; color: #9999aa; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;# app/Resources/views/Emails/sendemail.html.twig #}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;&amp;lt;h3&amp;gt;You did it&amp;lt;/h3&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;Hi &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt; name &lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;br0&quot; style=&quot;box-sizing: border-box; color: #777777; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;! Your Message is successfully Submitted.&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;odd&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 1px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;We will get back to you soon!&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;even&quot; style=&quot;background-color: white; border-color: rgb(255, 255, 255) rgb(255, 255, 255) rgb(255, 255, 255) rgb(224, 224, 224); border-image: initial; border-style: solid; border-width: 0px 0px 0px 1px; box-sizing: border-box; color: #aaaaaa; font-size: inherit; line-height: 16px; list-style: outside decimal; margin: 0px 0px 0px 40px; padding: 1px 5px 5px 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; color: black; font-family: inherit; font-size: 12px; line-height: inherit; margin: 0px; padding: 0px;&quot;&gt;Thanks!&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Now, when you submit the form, you will get an acknowledgment email in your inbox.&lt;/p&gt;&lt;p style=&quot;text-align: left;&quot;&gt;If you have a query or would like to contribute to the discussion, do leave a comment.&lt;/p&gt;&lt;/div&gt;</description><link>http://www.phpcmsframework.com/2022/02/create-contact-form-in-symfony-with-swiftmailer.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEiOU_aBVPLaRYwtECoM-IMOAAxVKl-p-Ho2hrcNzgI1EOL0bp9fEwBrpM7E2g1skbtbLkTLYsuysyEWQ-esl5ZgY1AXnZ5MOm0bEK0M1l88_0hbJYSzlkUM-GLlINxncE_hpOwRVxSqavoDdJ7rD-rgUrD5HsGUqm8MUv5rW1qh14xMIxS8JeTkaASk=s72-w400-h360-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-7089984723004688795</guid><pubDate>Sun, 30 Jan 2022 20:30:00 +0000</pubDate><atom:updated>2022-01-30T12:31:03.950-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Symfony</category><title>How to implement Functional Test in Symfony 4 with Kahlan 4</title><description>&lt;p&gt;In this tutorial, we are going to discuss about how to implement the functional testing in Symfony 4 with Kahlan 4. In IT world, whenever we are creating an application/website, we need to test the application/website thouroghly. To perform the functional testing we have a &lt;a href=&quot;https://github.com/elephantly/kahlan-bundle&quot; target=&quot;_blank&quot;&gt;bundle&lt;/a&gt; in Symfony 4.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The simplest way is define Symfony 4 skeleton bootstrap in kahlan config, and use its property at specs, for example, we configure config at &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;kahlan-config.php&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; as follows:&lt;/p&gt;&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;suite()-&amp;gt;root();
    $root-&amp;gt;beforeAll(function () {
        $this-&amp;gt;request = Request::createFromGlobals();
        $this-&amp;gt;kernel  = new Kernel(&#39;test&#39;, false);
    });
    return $next();
});&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Above settings are minimal, if you need more setup, you can define there. If you didn’t require &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;kahlan/kahlan:^4.0,&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; you can require via composer:&lt;/p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;$ composer require --dev kahlan/kahlan:^4.0&lt;/span&gt;&lt;/pre&gt;
&lt;h2&gt;Give a try&lt;/h2&gt;
&lt;p&gt;Let’s try testing a famous /lucky/number from &lt;a href=&quot;https://symfony.com/doc/current/page_creation.html&quot; target=&quot;_blank&quot;&gt;LuckyController&lt;/a&gt;. We have the following controller:&lt;/p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;render(&#39;lucky/number.html.twig&#39;, [
            &#39;number&#39; =&amp;gt; $number,
        ]);
    }
}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And our twig file is:&lt;/p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;{# templates/lucky/number.html.twig #}
&amp;lt;h1&amp;gt;Your lucky number is {{ number }}&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;We can place test under spec directory at root directory, for its test, we can create a spec/Controller directory:&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;kahlan.config.php&lt;br /&gt;
  &lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;spec&lt;br /&gt;
  &lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;Controller&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, we can create the test as follows with make request to the ‘/lucky/number’ page and get its response. We can use toMatchEcho matcher provided with regex to get match random number of mt_rand(0, 100) that printed inside a response html content:&lt;/p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;request-&amp;gt;create(&#39;/lucky/number&#39;, &#39;GET&#39;);
            $response = $this-&amp;gt;kernel-&amp;gt;handle($request);
 
            expect(function () use ($response) {
                $response-&amp;gt;send();
            })-&amp;gt;toMatchEcho(
                &quot;#Your lucky number is ([0-9]|[1-8][0-9]|9[0-9]|100)#&quot;
            );
 
        });
 
    });
 
});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Time to run it with command:&lt;/p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;$ vendor/bin/kahlan &lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;By running the above commmand, you will get an Output.&lt;/p&gt;
</description><link>http://www.phpcmsframework.com/2022/01/functional-test-in-symfony4-kahlan.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-2339398322533484532</guid><pubDate>Wed, 26 Jan 2022 19:20:00 +0000</pubDate><atom:updated>2026-03-18T11:48:46.164-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>Display personalised content in Drupal - Smart Content Paragraphs</title><description>&lt;p&gt;In this tutorial we are going to discuss about, how to display the personalised content (Paragraphs) for each person (both anonymous and authenticated users) in Drupal.&lt;/p&gt;&lt;div&gt;&lt;a href=&quot;https://www.drupal.org/project/smart_content&quot; target=&quot;_blank&quot;&gt;Smart content&lt;/a&gt; module allows personalised blocks only. If we need to display the personalised Paragraphs, then we need to go for &lt;a href=&quot;https://www.drupal.org/project/smart_content_paragraphs&quot; target=&quot;_blank&quot;&gt;Smart Content Paragraphs&lt;/a&gt; module which will supports Paragraphs as well.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Dependent Modules&amp;nbsp;&lt;/h3&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Paragraphs (https://www.drupal.org/project/paragraphs)&lt;/li&gt;&lt;li&gt;Smart Content (https://www.drupal.org/project/smart_content)&lt;/li&gt;&lt;li&gt;Smart Content Segments (https://www.drupal.org/project/smart_content_segments)&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.drupal.org/project/smart_content_paragraphs&quot; target=&quot;_blank&quot;&gt;Smart Content Paragraphs&lt;/a&gt; uses conditions, within Segments, to determine whether or not to display the reaction(s) defined within a Decision Block. A Condition is a single case that can be tested and determined to be either true or false, multiple conditions can be included in a Segment and a Segment Set is one or more Segments grouped together.&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://ftp.drupal.org/files/projects/smart_content_paragraphs-1.0.0-alpha3.tar.gz&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt; to Donwload the Module&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.drupal.org/project/smart_content_paragraphs/releases&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt; to view the releases&lt;/div&gt;</description><link>http://www.phpcmsframework.com/2022/01/display-personalised-content-in-drupal.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-1418033208782385068</guid><pubDate>Sun, 09 Sep 2018 17:53:00 +0000</pubDate><atom:updated>2026-03-18T11:48:52.522-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Drupal</category><title>Create custom theme in Drupal 8</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this tutorial we are going to discuss about, How to create custom theme (template) in Drupal 8 PHP CMS. By creating custom theme, we can customize the look and feel of the website as per our wish instead of using the pre-defined template or third party templates.&lt;br /&gt;
&lt;br /&gt;
To create a new template, we have to follow the following steps.&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Create YAML (.yml) file&lt;/li&gt;
&lt;li&gt;Edit YAML (.yml) file&lt;/li&gt;
&lt;li&gt;Remove stylesheets&lt;/li&gt;
&lt;li&gt;Clear the cache&lt;/li&gt;
&lt;li&gt;Optimize the Website&lt;/li&gt;
&lt;li&gt;Add CSS&lt;/li&gt;
&lt;li&gt;Add Javascript&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;Step 1: Create YAML (.yml) file&lt;/span&gt;&lt;/h2&gt;
&lt;div&gt;
In Drupal 8, YAML file (.yml) is used in the place of .INFO files (used in Drupal 7) to tell the website that the theme exists. Similarly, the directories that contain the theme and the files with theme details have also been changed.&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Create a folder with the theme name which we are going to create like (&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;) inside the &lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&amp;lt;root&amp;gt;/theme&lt;/i&gt;&lt;/span&gt;&lt;/b&gt; folder.&lt;/li&gt;
&lt;li&gt;Create a new file inside the folder with the name &lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;first_theme.info.yml&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 2: Edit YAML file&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
Open the YAML file in your preferred text editor and enter the following details:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;name: first_theme&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;description: Enter some description about your template&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;type: theme&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b style=&quot;background-color: white;&quot;&gt;&lt;span style=&quot;color: blue;&quot;&gt;core: 8.x&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Type is theme and core is the version of Drupal you are creating the theme for (In this is case it is Drupal 8, so 8.x).&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Now go to your Drupal website and check if the new theme appears in the Drupal appearance section. If all the steps have been correctly followed, the theme will appear in the uninstalled section of your website’s appearance tab.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Click &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: blue;&quot;&gt;install and set as default&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; to set this theme as default.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 3: Remove Stylesheets (Optional)&lt;/h2&gt;
&lt;div&gt;
After you have set the new theme as default, and then navigate to the website’s homepage, you will notice that nothing has changed. This is because Drupal includes several stylesheets that loads by default. In many cases, the best strategy is to disable some of these stylesheets.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To find out these stylesheets,&amp;nbsp; you will need to inspect the source code by right-clicking and selecting View Source from the context menu and determine which CSS files you wish to remove, the rest of the process is pretty straightforward.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Go back to the first_theme.info.yml file and edit it. To remove the stylesheets you want, add this text:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: blue;&quot;&gt;stylesheets-remove&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;-“stylesheet to be removed”&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 4: Clear the Cache&lt;/h2&gt;
&lt;div&gt;
Now login to your Drupal website’s admin panel. Next, go to C&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;onfiguration &amp;gt;&amp;gt; Performance&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; and click &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;Clear all caches&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 5: Optimize the Website&lt;/h2&gt;
&lt;div&gt;
Next, go back to the Performance page and uncheck Aggregate CSS files and Aggregate JavaScript files in the Bandwidth Optimization section. This will help in speeding up the performance of the website.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 6: Add CSS&lt;/h2&gt;
&lt;div&gt;
It is time to add reference to the CSS file that will be used for/by the theme. To do this, go to the theme’s folder and create a new file named &lt;b&gt;&lt;i&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme.libraries.yml&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; and update the file with css files which we are going to use in the theme.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;global-css:&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;css:&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; theme:&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;css/style.css:{}&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&amp;nbsp; &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
Now, add the library in the &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme.info.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; file as well. To do this, add the below code in the file.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;libraries:&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp;-first_theme/global-css&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
Step 7: Add JavaScript Reference&lt;/h2&gt;
&lt;div&gt;
To add reference to the JavaScript reference to the theme, add the below code snippet to the &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme.libraries.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; file.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;global-js:&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp;js:&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; js/site.js:{}&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp;dependencies:&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; -core/jquery&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
Now, add the the JavaScript library in the &lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme.info.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; file.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;-first_theme/global-js&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Finally the&amp;nbsp;&amp;nbsp;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;first_theme.info.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt; will looks like below,&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;name: first_theme&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;description: Enter some description about your template&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;type: theme&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;core: 8.x&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;libraries:&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;-first_theme/global-css&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;-first_theme/global-js&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><link>http://www.phpcmsframework.com/2018/09/create-custom-theme-in-drupal-8.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-939032303642122392</guid><pubDate>Wed, 14 Oct 2015 16:26:00 +0000</pubDate><atom:updated>2015-10-14T09:26:57.144-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CakePHP</category><title>XML Views in CakePHP</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this article, we are going to discuss about how to return XML response to views in CakePHP. CakePHP is an open source web application framework. It follows the Model-View-Controller (MVC) approach and is written in PHP, modeled after the concepts of Ruby on Rails, and distributed under the MIT License.CakePHP uses well-known software engineering concepts and software design patterns, such as Convention over configuration, Model-View-Controller, Active Record, Association Data Mapping, and Front Controller.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Step 1:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Open the file &quot;&lt;b&gt;&lt;i&gt;app\Config\routes.php&lt;/i&gt;&lt;/b&gt;&quot; and added the following line:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;/**&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;* Parse XML Routes&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;*/&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;Router::parseExtensions(&#39;xml&#39;);&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This enables your Controller actions to start accepting the .xml postfix e.g. &lt;b&gt;http://cakephp_app/controller/action.xml&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Step 2:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Enable the &lt;b&gt;&lt;i&gt;RequestHandler &lt;/i&gt;&lt;/b&gt;component in your Controller like this:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;public $components = array(&#39;RequestHandler&#39;);&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Step 3:&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Once that&#39;s done, you have a few more methods at your disposal to start dealing with XML requests. First create a &lt;b&gt;&lt;i&gt;beforeFilter &lt;/i&gt;&lt;/b&gt;method in your Controller and add the following:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;public function beforeFilter() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; parent::beforeFilter();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; // Set XML&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; if ($this-&amp;gt;RequestHandler-&amp;gt;isXml()) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$this-&amp;gt;RequestHandler-&amp;gt;setContent(&#39;xml&#39;);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Step 4:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Once that&#39;s done, create the action that you want to use and be sure to add in the &lt;b&gt;&lt;i&gt;respondAs &lt;/i&gt;&lt;/b&gt;&amp;amp; &lt;b&gt;&lt;i&gt;renderAs &lt;/i&gt;&lt;/b&gt;methods as the official documentation is a bit flakey with their use:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;public function related() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; // Only allow XML requests&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; if (!$this-&amp;gt;RequestHandler-&amp;gt;isXml()) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw new MethodNotAllowedException();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; &amp;nbsp; // Set response as XML&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $this-&amp;gt;RequestHandler-&amp;gt;respondAs(&#39;xml&#39;);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $this-&amp;gt;RequestHandler-&amp;gt;renderAs($this, &#39;xml&#39;);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Step 5:&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Using those 2 methods as I was then able to create an xml folder in the corresponding View folder and inside that create my view file e.g. &lt;b&gt;&lt;i&gt;app\View\Uploads\xml\related.ctp&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;lt;people&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&amp;lt;person&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;&amp;lt;name&amp;gt;James&amp;lt;/name&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&amp;lt;/person&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;lt;/people&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Now if you visited the page in your browser you should see your XML output as per your View e.g. &lt;b&gt;http://cakephp_app/uploads/related.xml&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Wrapping Up&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Further to this if you wanted to pass in some parameters to make the Controller action dynamic you can do by using the following e.g. http://cakephp_app/uploads/related/videos/1.xml&lt;br /&gt;
&lt;br /&gt;
The &quot;videos&quot; parameter and the &quot;1&quot; ID will be available in the Controller like this:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: #a64d79;&quot;&gt;&lt;b&gt;// Get passed params&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;$uploadType = $this-&amp;gt;request-&amp;gt;params[&#39;pass&#39;][0];&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;$uploadId = $this-&amp;gt;request-&amp;gt;params[&#39;pass&#39;][1];&lt;/span&gt;&lt;/div&gt;
</description><link>http://www.phpcmsframework.com/2015/10/xml-views-in-cakephp.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-4984915010437157269</guid><pubDate>Wed, 23 Sep 2015 16:26:00 +0000</pubDate><atom:updated>2026-03-18T11:49:00.111-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Wordpress</category><title>Automatically Create a Bit.ly URL for WordPress Posts</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
In this article, we are going to discuss about How to create Bit.ly URL for WordPress posts automatically. Bit.ly is currently the most popular URL shorting service out there and for a good reason too. With bit.ly you can track your shortened URLs and much more. In this article I&#39;m going to show you how to use bit.ly&#39;s api create bit.ly urls automatically for WordPress posts.&lt;br /&gt;
&lt;br /&gt;
In order to make use of Bit.ly&#39;s API, you&#39;ll need to:&lt;br /&gt;
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://bit.ly/&quot; target=&quot;_blank&quot;&gt;Signup at Bit.ly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://bit.ly/account/your_api_key/&quot; target=&quot;_blank&quot;&gt;Get your API Key&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
Now that you have a login and API key, open your WordPress theme&#39;s &lt;i&gt;&lt;b&gt;functions.php &lt;/b&gt;&lt;/i&gt;(just create one if you don&#39;t have one) and paste the following code at the top of the document:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;//create bit.ly url&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;function bitly()&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;{&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;&amp;nbsp; //login information&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $url = get_permalink(); &amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;//generates wordpress&#39; permalink&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $login = &#39;imjp&#39;; &amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;&amp;nbsp;//your bit.ly login&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $apikey = &#39;R_11882237eac772b5d6126e895a06c43f&#39;; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;//bit.ly apikey&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $format = &#39;json&#39;; &amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;//choose between json or xml&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $version = &#39;2.0.1&#39;;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;&amp;nbsp; //create the URL&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $bitly = &#39;http://api.bit.ly/shorten?version=&#39;.$version.&#39;&amp;amp;longUrl=&#39;.urlencode($url).&#39;&amp;amp;login=&#39;.$login.&#39;&amp;amp;apiKey=&#39;.$apikey.&#39;&amp;amp;format=&#39;.$format;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; //get the url&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt;&amp;nbsp; //could also use cURL here&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; $response = file_get_contents($bitly);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; //parse depending on desired format&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; if(strtolower($format) == &#39;json&#39;)&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $json = @json_decode($response,true);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo $json[&#39;results&#39;][$url][&#39;shortUrl&#39;];&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; else&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #a64d79;&quot;&gt; //xml&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $xml = simplexml_load_string($response);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; echo &#39;http://bit.ly/&#39;.$xml-&amp;gt;results-&amp;gt;nodeKeyVal-&amp;gt;hash;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: blue;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The code above is pretty much self explanatory.&lt;br /&gt;
&lt;br /&gt;
Don&#39;t forget to change the login and apikey strings to match yours.&lt;br /&gt;
&lt;br /&gt;
I hope you guys found this article useful.&lt;/div&gt;
</description><link>http://www.phpcmsframework.com/2015/09/automatically-create-bitly-url-for.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-310346853756243855.post-335417871391891636</guid><pubDate>Wed, 02 Sep 2015 16:28:00 +0000</pubDate><atom:updated>2026-03-18T11:47:57.571-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Joomla</category><title>TZ Portfolio - A joomla portpolio component / Module / Plugin download</title><description>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
TZ Portfolio works on database of comcontent, sothat you do not have to worry about importing or exporting data from your system (which already works with comcontent). TZ Portfolio inherits all current functions of com_content, in addition, we develop two new data interfaces: Portfolio and Timeline view. TZ Portfolio is strongly supported by Group Extra field system, you can create multi-portfolio system in your website. In addition, it supply 3 functions , these are video display, gallery or representative photo displayed for each article.&lt;br /&gt;
&lt;br /&gt;
We also upgrade tag and authority information management function, along with photo smart resize and crop. With TZ Portfolio you can own a smart blog, a flexible portfolio, and more than a complete content management system.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Extension Name:&lt;/b&gt; TZ Portfolio&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Price:&lt;/b&gt; Free&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;More info and reviews:&lt;/b&gt; &lt;a href=&quot;http://extensions.joomla.org/extensions/extension/authoring-a-content/content-construction/tz-portfolio&quot; target=&quot;_blank&quot;&gt;TZ Portfolio on JED&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;For Demo&lt;/b&gt; - &lt;a href=&quot;http://demo.tzportfolio.com/&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;To download the component&lt;/b&gt; - &lt;a href=&quot;https://www.templaza.com/tz_membership/downloadinfo/65-tz-portfolio-package-3&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;For Documentation&lt;/b&gt; - &lt;a href=&quot;http://www.templaza.com/manuals/category/joomla-extensions/tz-portfolio/&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;For Support&lt;/b&gt; - &lt;a href=&quot;http://www.templaza.com/Forum/tz-portfolio&quot; target=&quot;_blank&quot;&gt;Click Here&lt;/a&gt;&lt;/div&gt;
</description><link>http://www.phpcmsframework.com/2015/09/tz-portfolio-joomla-portpolio-component.html</link><author>noreply@blogger.com (PHP CMS Frameworks)</author><thr:total>0</thr:total></item></channel></rss>