<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Drupal Blog]]></title>
        <description><![CDATA[RSS feed for all Drupal blog posts.]]></description>
        <link>https://blog.hrishikeshdalal.tech/drupal/drupal</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Tue, 16 Jun 2026 09:45:37 GMT</lastBuildDate>
        <atom:link href="https://blog.hrishikeshdalal.tech/drupal/drupal/rss.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Tue, 16 Jun 2026 09:45:35 GMT</pubDate>
        <language><![CDATA[en]]></language>
        <ttl>60</ttl>
        <item>
            <title><![CDATA[Drupal x Google Summer of Code: Reuse & Previews]]></title>
            <description><![CDATA[Week 3 of my GSoC journey with Drupal: I focused on building a reusable YAML extractor and validator utility to streamline AI response processing, along with minor UI improvements for the recipe generator.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a2f96118bbe21d5504f21c2</link>
            <guid isPermaLink="false">6a2f96118bbe21d5504f21c2</guid>
            <pubDate>Mon, 15 Jun 2026 06:05:05 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>WELLLLCOMMEE to Week 3 of this series!</strong></p>
<p>In the last week made quite a few structural changes that will help us. Have created the YAML extractor and parser which can extract the YAML component form any response of the LLM using Symfony to validate it. Made a Utility for this thereby making the code reusable. </p>
<p>Official Issue Tracker: <a href="https://www.drupal.org/project/ai_recipe_generator/issues/3594062">Week 3 Tracker</a></p>
<p>Let me now give me glimpse for this. </p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781504252/drupal/u7akwfg75woresztzmje.png" alt="The Output Preview &amp; Validator" /><figcaption>The Output Preview &amp; Validator</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781504306/drupal/boxwpkoc1vmuvigbb6zb.png" alt="UI Changes" /><figcaption>Tiny UI Changes</figcaption></figure>
<blockquote>
<p>Time to Dive Deep into this</p>
</blockquote>
<h2>YAML Validator &amp; Extractor</h2>
<p>Created a Utility folder under src and created a file named Yaml Processor. The concept of this file was simple, when you get an output from LLM, from whichever Service or Function you pull this from this will extract the YAML and give it to you.</p>
<blockquote>
<p>Here it uses this : use Drupal\Component\Serialization\Yaml</p>
</blockquote>
<p>This under the hood uses Symfony which helps to validate and extraction.  Symfony is the underlying PHP framework that powers modern Drupal ( this is just one of the usecase)</p>

<pre><code class="language-php">&lt;?php

namespace Drupal\ai_recipe_generator\Utility;

use Drupal\Component\Serialization\Yaml;

/**
 * Utility class to parse and extract YAML from AI responses.
 */
class YamlProcessor {

  /**
   * Cleans a response down to YAML text when possible.
   *
   * @param string $response
   *   The raw model response.
   *
   * @return string
   *   A normalized YAML string or the response with code fences removed.
   */
  public static function normalize(string $response): string {
    $parsed = self::extractAndParse($response);

    if (is_array($parsed)) {
      return trim(Yaml::encode($parsed));
    }

    $cleaned = preg_replace(&#39;/```(?:yaml|yml)?\s*|```/i&#39;, &#39;&#39;, $response) ?? $response;
    return trim($cleaned);
  }

  /**
   * Attempts to extract and validate YAML from a given string.
   *
   * @param string $response
   *   The raw string response.
   *
   * @return array|null
   *   The parsed YAML as an array, or null if no valid YAML could be extracted.
   */
  public static function extractAndParse(string $response): ?array {
    // First try parsing the entire response as YAML.
    try {
      $parsed = Yaml::decode($response);

      if (is_array($parsed)) {
        return $parsed;
      }
    }
    catch (\Exception $e) {
      // Not valid YAML, continue with extraction.
    }

    // Extract YAML from markdown code blocks.
    $pattern = &#39;/```(?:yaml|yml)?\s*(.*?)```/is&#39;;

    if (preg_match($pattern, $response, $matches)) {
      $yaml = trim($matches[1]);

      try {
        $parsed = Yaml::decode($yaml);

        if (is_array($parsed)) {
          return $parsed;
        }
      }
      catch (\Exception $e) {
        return NULL;
      }
    }

    return NULL;
  }

}
</code></pre>
<h2>Reusable YAML Previewer</h2>
<p>If you have seen my previous blog, the older YAML previewers they were not upto the mark. So improved that with having better parsers and lines. What I used for this was a template, using a html.twig format which helped to render properly anywhere, anyhow!</p>
<p>This is slightly difficult to think and then implement (tedious slightly) but since highly reusable, highly useful!</p>

<pre><code class="language-plaintext">{#
/**
 * @file
 * Template for the fluid YAML previewer component with line numbers.
 */
#}
&lt;div class=&quot;ai-yaml-preview-container&quot; {{ attributes }}&gt;
  &lt;div class=&quot;ai-yaml-preview-header&quot;&gt;
    &lt;span class=&quot;ai-yaml-preview-title&quot;&gt;{{ title|default(&#39;YAML Output Preview&#39;) }}&lt;/span&gt;
    
    &lt;button class=&quot;ai-yaml-copy-btn&quot; onclick=&quot;
      const container = this.closest(&#39;.ai-yaml-preview-container&#39;);
      const code = container.querySelector(&#39;code&#39;).innerText;
      navigator.clipboard.writeText(code);
      const originalText = this.innerText;
      this.innerText = &#39;Copied!&#39;;
      setTimeout(() =&gt; this.innerText = originalText, 2000);
    &quot;&gt;Copy YAML&lt;/button&gt;
  &lt;/div&gt;
  
  &lt;div class=&quot;ai-yaml-preview-body&quot;&gt;
    {# This empty div will be filled with numbers by the JS below #}
    &lt;div class=&quot;ai-yaml-line-numbers&quot; aria-hidden=&quot;true&quot;&gt;&lt;/div&gt;
    
    &lt;pre class=&quot;ai-yaml-preview-content&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;{{ yaml_string }}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

{# Inline script to generate line numbers based on the code length #}
&lt;script&gt;
  (function() {
    // Find all preview containers on the page
    const containers = document.querySelectorAll(&#39;.ai-yaml-preview-container&#39;);
    
    containers.forEach(container =&gt; {
      const codeBlock = container.querySelector(&#39;code&#39;);
      const linesContainer = container.querySelector(&#39;.ai-yaml-line-numbers&#39;);
      
      // Only process if it hasn&#39;t been filled yet
      if (codeBlock &amp;&amp; linesContainer &amp;&amp; linesContainer.innerHTML === &#39;&#39;) {
        // Split by newline to get total lines
        const lines = codeBlock.innerText.split(&#39;\n&#39;);
        
        // Handle the case where the AI leaves a trailing empty line at the end
        const lineCount = lines[lines.length - 1] === &#39;&#39; ? lines.length - 1 : lines.length;
        
        let numbersHTML = &#39;&#39;;
        for (let i = 1; i &lt;= Math.max(1, lineCount); i++) {
          numbersHTML += i + &#39;&lt;br&gt;&#39;;
        }
        linesContainer.innerHTML = numbersHTML;
      }
    });
  })();
&lt;/script&gt;</code></pre>
<h2>Code Reduction</h2>
<p>Let&#39;s understand the problem, we had 3 files for calling the LLM models &amp; providers. All of them had separate functions. So if I ever wanted to change anything, I would have to change in all the three files : (</p>
<p>So we changed the calling part. for 2 (ArenaService &amp; RecipeGenerationService) of them as they were the same replicas the third one at BenchmarkDashboardService was a different one. Clubbing them was fairly simple, just had to change the function calls. </p>

<h2>Started fixing the Pipeline</h2>
<p>Did the cspell checking and then the phpcs checking which was initially failing but then started fixing the same. For fixing the cspell introduced a file called: .cspell.json. This needed to have all the words in the document which I wanted to exclude form the checking.</p>
<p>Will write a separate blog on testing!</p>

<pre><code class="language-json">{
    &quot;version&quot;: &quot;0.2&quot;,
    &quot;language&quot;: &quot;en&quot;,
    &quot;words&quot;: [
        &quot;ai_recipe_generator&quot;,
        &quot;chartjs&quot;,
        &quot;crosshairs&quot;,
        &quot;dalal&quot;,
        &quot;fontawesome&quot;,
        &quot;hrishikesh&quot;,
        &quot;llm&quot;,
        &quot;llms&quot;,
        &quot;multichat&quot;,
        &quot;onclick&quot;,
        &quot;Starshot&quot;,
        &quot;tempstore&quot;,
        &quot;unconfigured&quot;,  
        &quot;xaxis&quot;,
        &quot;xmark&quot;,
        &quot;yaml&quot;,
        &quot;yaxis&quot;
    ],
    &quot;ignorePaths&quot;: [
        &quot;vendor/**&quot;,
        &quot;node_modules/**&quot;,
        &quot;*.min.js&quot;,
        &quot;*.min.css&quot;
    ]
}</code></pre>
<blockquote>
<p>Thank you &amp; catch you up next time!</p>
</blockquote>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781509663/drupal/rwbkegeqyhsy7ucahzmo.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Drupal: Schema's & Databases]]></title>
            <description><![CDATA[Learn how Drupal manages database schemas using .install files and hook_schema() instead of raw SQL, illustrated with a practical AI Recipe Generator module example.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a2d8f1fcb0f513275eaa3f7</link>
            <guid isPermaLink="false">6a2d8f1fcb0f513275eaa3f7</guid>
            <pubDate>Sat, 13 Jun 2026 17:10:55 GMT</pubDate>
            <content:encoded><![CDATA[<p>Whenever you want to use the database to store something, or if you want to have some custom information, a database will always be your go to option :)</p>
<p>Drupal has a really interesting way for databases. </p>
<p>So rather than writing the direct SQL statements, which would be tedious and boring, it uses a <strong>.install file</strong> to handle this. This translates into a proper query that we are going to use, which is useful for storing structured information. </p>
<p>Let us go through this with help of an example.</p>

<pre><code class="language-plaintext">&lt;?php

/**
 * @file
 * Install, update and uninstall functions for the AI Recipe Generator module.
 */

/**
 * Implements hook_schema().
 */
function ai_recipe_generator_schema() {
  $schema[&#39;ai_recipe_generator_benchmarks&#39;] = [
    &#39;description&#39; =&gt; &#39;Stores historical benchmark metrics for AI providers.&#39;,
    &#39;fields&#39; =&gt; [
      &#39;id&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;serial&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;description&#39; =&gt; &#39;Primary Key.&#39;,
      ],
      &#39;provider&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;varchar_ascii&#39;,
        &#39;length&#39; =&gt; 64,
        &#39;not null&#39; =&gt; TRUE,
        &#39;default&#39; =&gt; &#39;&#39;,
      ],
      &#39;model&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;varchar_ascii&#39;,
        &#39;length&#39; =&gt; 64,
        &#39;not null&#39; =&gt; TRUE,
        &#39;default&#39; =&gt; &#39;&#39;,
      ],
      &#39;prompt&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;text&#39;,
        &#39;size&#39; =&gt; &#39;big&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;description&#39; =&gt; &#39;The user prompt submitted to the AI.&#39;,
      ],
      &#39;response&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;text&#39;,
        &#39;size&#39; =&gt; &#39;big&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;description&#39; =&gt; &#39;The raw generated text/YAML from the LLM.&#39;,
      ],
      &#39;latency&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;int&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;description&#39; =&gt; &#39;Total response time in milliseconds.&#39;,
      ],
      &#39;yaml_valid&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;int&#39;,
        &#39;size&#39; =&gt; &#39;tiny&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;default&#39; =&gt; 0,
        &#39;description&#39; =&gt; &#39;1 if clean or recovered via sanitizer, 0 if completely unparsable.&#39;,
      ],
      &#39;schema_compliant&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;int&#39;,
        &#39;size&#39; =&gt; &#39;tiny&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;default&#39; =&gt; 0,
        &#39;description&#39; =&gt; &#39;1 if all required recipe keys are present.&#39;,
      ],
      &#39;timestamp&#39; =&gt; [
        &#39;type&#39; =&gt; &#39;int&#39;,
        &#39;not null&#39; =&gt; TRUE,
        &#39;default&#39; =&gt; 0,
      ],
    ],
    &#39;primary key&#39; =&gt; [&#39;id&#39;],
    &#39;indexes&#39; =&gt; [
      &#39;provider_model&#39; =&gt; [&#39;provider&#39;, &#39;model&#39;],
    ],
  ];

  return $schema;
}</code></pre>
<p>Here as you can see the name is ai_recipe_generator.install (i.e. name of your module)</p>
<p>First let&#39;s understand what is a hook_schema().</p>
<p>hook_schema() is a specific function that allows a module to define its own custom database tables. It serves as a blueprint or map that tells Drupal exactly how to structure data storage for that module.</p>
<p>Now the next thing that would come to your mind is - What does this do anyways?</p>
<h2>The ORM</h2><p>It acts like a ORM - Object Relational Mapper. Let&#39;s us take a step back and understand what an ORM is. </p>
<blockquote>
<p>The Object-Oriented World (Your Code): Written in languages like PHP, Python, or JavaScript, where data is treated as interconnected objects with properties and methods.</p>
</blockquote>
<blockquote>
<p>The Relational World (Your Database): Written in SQL (MySQL, PostgreSQL), where data is stored in rigid, flat tables consisting of rows and columns.</p>
</blockquote>
<p>So to simply put</p>
<p><strong>Without an ORM</strong>:  Manually write the SQL queries</p>
<p><strong>With an ORM</strong>: System writes SQL queries for you.</p>
<p>The ORM helps you write queries lightning fast &amp; good ORMs have security out of the box. The only disadvantage however is that they add another layer of abstraction which can sometimes slow down the speed (in case of a bloated SQL).</p>

<p>Coming back to the .install file.</p>
<p>We can say that</p>
<p>A Recipe class maps to recipe table</p>
<p>$recipe-&gt;title maps to a title column</p>
<p>Also helps to decide the NOT NULL fields &amp; Primark Keys.</p>
<p>Further this can automatically handles table creation + deletion on installing &amp; uninstalling. Simple isn&#39;t it?</p>

<h2>Some good toknowinfo</h2><p><strong>Standard Documentation Name:</strong> <code>hook_install()</code>
<strong>What you write for your module:</strong> <code>ai_recipe_generator_install()</code>
<strong>When Drupal runs it:</strong> Runs after the database table is created, perfect for inserting default data.</p>
<p><strong>Standard Documentation Name:</strong> <code>hook_uninstall()</code>
<strong>What you write for your module:</strong> <code>ai_recipe_generator_uninstall()</code>
<strong>When Drupal runs it:</strong> Runs when removing the module, perfect for clearing out configuration settings.</p>
<p><strong>Standard Documentation Name:</strong> <code>hook_update_N()</code>
<strong>What you write for your module:</strong> <code>ai_recipe_generator_update_10001()</code>
<strong>When Drupal runs it:</strong> Runs when a developer executes database updates on a live site.</p>

<p>The different types of keys in schemas.</p>
<h3>Table-level keys</h3>
<ul>
<li><code>description</code>: A short explanation of what the table stores and its purpose.</li>
<li><code>fields</code>: The list of columns that make up the table.</li>
<li><code>primary key</code>: The column(s) used to uniquely identify each record.</li>
<li><code>unique keys</code>: The column combinations that must contain unique values.</li>
<li><code>foreign keys</code>: The relationships between this table and other tables.</li>
<li><code>indexes</code>: The columns that are indexed to improve query performance.</li>
</ul>
<h3>Field specification keys</h3>
<ul>
<li><code>description</code>: A brief explanation of what the field represents.</li>
<li><code>type</code>: The kind of data the field stores, such as text or numbers.</li>
<li><code>mysql_type</code>: A custom data type definition specifically for MySQL.</li>
<li><code>pgsql_type</code>: A custom data type definition specifically for PostgreSQL.</li>
<li><code>sqlite_type</code>: A custom data type definition specifically for SQLite.</li>
<li><code>serialize</code>: Indicates whether the field value is stored in serialized format.</li>
<li><code>size</code>: Defines the expected size range of the data stored in the field.</li>
<li><code>not null</code>: Specifies whether the field is required and cannot be empty.</li>
<li><code>default</code>: The value automatically assigned when none is provided.</li>
<li><code>length</code>: The maximum number of characters allowed in text fields.</li>
<li><code>unsigned</code>: Restricts numeric values to non-negative numbers.</li>
<li><code>precision</code>: The total number of digits allowed in a numeric value.</li>
<li><code>scale</code>: The number of digits allowed after the decimal point.</li>
<li><code>binary</code>: Enables case-sensitive storage and comparison for text fields.</li>
</ul>
<h3>Foreign key specification keys</h3>
<ul>
<li><code>table</code>: The name of the table being referenced.</li>
<li><code>columns</code>: The mapping between local columns and referenced columns.</li>
</ul>

<p>Sources</p>
<p><a href="https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21database.api.php/group/schemaapi/11.x">Drupal Schema</a></p>
<p><a href="https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21database.api.php/function/implementations/hook_schema/11.x">Functions in Drupal Schema</a></p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781413444/drupal/ujkq9n25ftgikrtegrbq.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Summer of Code (GSoC) x Drupal: Week 2 - Mapping the Dashboards]]></title>
            <description><![CDATA[A weekly recap of GSoC Week 2 with Drupal, focusing on building the LLM Benchmarking Dashboard and LLM Arena to evaluate model performance, latency, and accuracy.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a2d645f2a057c0c604fe02c</link>
            <guid isPermaLink="false">6a2d645f2a057c0c604fe02c</guid>
            <pubDate>Sat, 13 Jun 2026 14:08:31 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>WELCOMMMEEEE to another episode of Weekly Recap of my project</strong></p>
<p>This week we have built two things: LLM Benchmarking Dashboard &amp; LLM Arena to show you which ones are the best. Both of these will be super useful while building our final product. </p>
<p>The Offical Issue Tracker: <a href="https://www.drupal.org/project/ai_recipe_generator/issues/3593030">Week 2 Issues Tackled</a>
Let me give you a glipmse of what our output will look like. </p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781359578/drupal/njlkdbbcehma9d6ynwqn.png" alt="Dashboard" /><figcaption>Dashboard (Part 1)</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781359606/drupal/eviouu8wsj4qyeptvtb9.png" alt="Dashboard " /><figcaption>Dashboard (Part 2)</figcaption></figure>
<h2>The Benchmarking Dashboard</h2><p>In order to capture the true essence of which model performs the best in the long run, had made a LLM benchmarking dashboard. Someone just by looking at charts can capture which model is most suitable for their usecase. </p>
<blockquote>Metrics that provided the overall picture</blockquote><p><strong>1). The Top Banner</strong>: It had the total runs so far, along with the average response time and the average accuracy for the recipe generated. </p>
<p><strong>2). The Visuals</strong>: Any dashboard is incomplete without a good visual. The first graph which shows the latency for the previous runs mapped across models is useful for finding the trend for model hits. The second graph is for using a bar graph for visualising the model avg. latency to compare which model is the faster as compared to others. Used the Chart module for the same. </p>
<p><strong>3). Key Metric Table</strong>: This is the detailed table for per prompt summary. It covers: <em>the provider, the model, the speed, whether the YAML is valid or not, whether the Schema proper or not as per Drupal Symfony, &amp; finally tells us about the accuracy along with timestamps</em>.</p>
<p>What is useful in this is that you can filter it along with sort the columns in ascending or descending order. Is if further <strong>colour coded</strong> so that you can quickly view the page at a glance. </p>
<p>Things I used to build this:</p>
<ul>
  <li>Chart.js / Drupal Chart Module: To render the responsive, interactive graphs.</li>
  <li>Custom Database Schema (.install): To reliably save and query the historical benchmarking data.</li>
  <li>Drupal AJAX API: To ensure the table filters and dynamic sorting update seamlessly.</li>
  <li>Font Awesome Library: To have cool icons in the dashboard</li>
</ul>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781359687/drupal/wy3fh91hwqzyz2losvre.png" alt="LLM Arena" /><figcaption>LLM Arena</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781359705/drupal/bfw3ernwat1hecyakw4f.png" alt="Drupal image" /></figure>
<h2>The LLM Arena </h2><p>This was particularly fun to build as this was for comparing the different responses. You get to type the custom prompt and then fire it across all the different models and providers. </p>
<blockquote>
<p>You can have all the different models &amp; providers, there is NO LIMIT!!</p>
</blockquote>
<p>Further these is also an option to store the responses in the database so that they reflected in the schema. There is also a delete button for the same. If you want to remove some model-provider.  Further you will see a very familiar screen that, while firing the prompts, it will look like new module install screen, this is because of the Batch API Screen. </p>
<p>Sources I used:</p>
<ol>
  <li>AJAX API: Model - Provider dropdown</li>
  <li>Batch API: For firing all the prompts together</li>
  <li>Font Awesome Icons: For the icons there</li>
</ol>
<h2>Laying out the Structure</h2>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781366121/drupal/ibcpl1lpkc6phhbhchid.png" alt="System Diagram" /><figcaption>System Diagram </figcaption></figure>
<p>Just like a good Drupal Practice (have to mention), have used AI to generate the image above. </p>
<h3>1. Client (User Browser) Layer</h3>
<p>Handles the user interface and visual presentation.</p>
<ul>
<li><p><strong>User (Arena/Dashboard Interface):</strong> Renders the comparison forms, interactive data tables, and metrics dashboards.</p>
</li>
<li><p><strong>Frontend Components:</strong></p>
</li>
<li><p><strong>JavaScript:</strong> Uses <code>Chart.js</code> to render interactive line and bar charts for model latency trends.</p>
</li>
<li><p><strong>Icons:</strong> Integrates <strong>Font Awesome</strong> for immediate visual cues (success/failure statuses).</p>
</li>
<li><p><strong>CSS:</strong> Uses <code>css/arena.css</code> and <code>css/dashboard.css</code> via <code>.libraries.yml</code> for custom, polished layouts.</p>
</li>
<li><p><strong>Client-Server Communication:</strong></p>
</li>
<li><p><strong>AJAX API:</strong> Controls non-blocking form updates, dynamic filtering, and column sorting without page reloads.</p>
</li>
<li><p><strong>Batch API (Client-Side):</strong> Drives the real-time progress bar (similar to the module installation screen) during execution.</p>
</li>
</ul>
<h3>2. Drupal Application Logic Layer</h3>
<p>The PHP backend that processes requests and orchestrates module logic.</p>
<ul>
<li><strong>Form API (<code>ArenaForm.php</code>, <code>BenchmarkDashboardForm.php</code>):</strong> Handles form submissions and handles backend execution triggers.</li>
<li><strong>Services Layer (<code>ArenaService.php</code>, <code>BenchmarkDashboardService.php</code>):</strong> Houses decoupled, reusable core logic registered via <code>ai_recipe_generator.services.yml</code>.</li>
<li><strong>API Integration Engine:</strong></li>
<li><strong>Drupal Batch API:</strong> Splits concurrent LLM requests into isolated chunks to prevent gateway timeouts and server lockups.</li>
<li><strong>Drupal AJAX API:</strong> Manages seamless, lightweight data passing between backend services and frontend components.</li>
</ul>
<h3>3. Data &amp; External Integrations Layer</h3>
<p>Manages persistent storage and outbound network requests.</p>
<ul>
<li><strong>Local Database (<code>.install</code>):</strong> Uses a custom schema defined in the install file to save historical run stats, latency, timestamps, and schema validation flags.</li>
<li><strong>External LLM APIs:</strong> Handles outbound calls to external models (OpenAI, Anthropic, Gemini). Isolated by the Batch API so a timeout on one provider doesn&#39;t crash the session.</li>
</ul>

<blockquote>
<p>Thanks a lot for reading!!</p>
</blockquote>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1781367505/drupal/yxmclfawvcdjd6xz702d.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Drupal x GSoC: Week 1 - Building the Base]]></title>
            <description><![CDATA[In the first week of my GSoC journey with Drupal, I focused on building the project foundation, setting up the module structure, and configuring essential routing, services, and form files for the AI Recipe Generator.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a2116768ded1b4d1690c224</link>
            <guid isPermaLink="false">6a2116768ded1b4d1690c224</guid>
            <pubDate>Thu, 04 Jun 2026 06:08:54 GMT</pubDate>
            <content:encoded><![CDATA[<p><strong>WELLLLLLCOMMEE To Week 1 of GSoC x Drupal.</strong></p>
<p>Over the first week my focus was on establishing a proper base for the project. This was particularly fun as you can see your project come to life. These are some other chapters of the journal that you should definitely read!</p>
<p>I created a project: <a href="https://blog.hrishikeshdalal.tech/drupal/6a1bbb83a9efe6ea5bb97c16">Creating a Module Project on Drupal (NO CODE) (With BONUS Section)</a></p>
<p>I learnt about <a href="https://blog.hrishikeshdalal.tech/drupal/6a200d7d5f3958f4a28782d7">PIRLS - Drupal&#39;s Core .yml files</a></p>
<p>Before you proceed any further, you should have a drupal project set up. If not, you can check it out here: <a href="https://blog.hrishikeshdalal.tech/drupal/6a21075f8d7f810de39a3084">Installing Drupal</a></p>
<p>Have added a bit more changes too, you can check it our here: <a href="https://www.drupal.org/project/ai_recipe_generator/issues/3592993">Week 1 Official Tracker</a></p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780549566/drupal/fgbvaxq71maydgairfiy.png" alt="Week 1 Issues Tackled" /><figcaption>Week 1 Issues Tackled</figcaption></figure>
<p>Let&#39;s zoom out and see what we have built. UI might not be that great yet but that was not the focus of the first week.</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780557278/drupal/fdy8j8sr3yd6p15nse3r.png" alt="Drupal image" /></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780557387/drupal/pmsx6pjzanp3mvpneaie.png" alt="Drupal image" /></figure>
<p>You should clone the basic repo of the module and paste it in: <strong>web/modules/custom/ai_recipe_generator</strong>.</p>
<p>You might need to create this folder &amp; add all the files. We use custom as we haven&#39;t made a deployment of the project yet. So once I did this I made. Now I have added ai_recipe_generator.info.yml, ai_recipe_generator_services.yml, ai_recipe_generator.routing.yml. I have made a blog on these files and what do these mean, you can check that out too. </p>
<p>I then created 2 things in src folder - Form &amp; Services. </p>
<p>So we have two pages as you saw above. Let&#39;s understand the routing or the file urls</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780557856/drupal/bihlzh3zc9xi3fhuvead.png" alt="Our File Structure" /><figcaption>Our File Structure</figcaption></figure>
<pre><code class="language-yaml">ai_recipe_generator.test_form:
  path: &#39;/admin/config/development/ai-recipe-test&#39;
  defaults:
    _form: &#39;\Drupal\ai_recipe_generator\Form\RecipeGeneratorTestForm&#39;
    _title: &#39;AI Recipe Generator (Test UI)&#39;
  requirements:
    _permission: &#39;administer site configuration&#39;

ai_recipe_generator.settings:
  path: &#39;/admin/config/ai-recipe/settings&#39;
  defaults:
    _form: &#39;\Drupal\ai_recipe_generator\Form\SettingsForm&#39;
    _title: &#39;AI Recipe Generator Settings&#39;
  requirements:
    _permission: &#39;administer site configuration&#39;</code></pre>
<p>Each entry has the following</p>
<p>1 The URL Path</p>
<p>2 The Form File which it should render</p>
<p>3 The title of the Page</p>
<p>4 Permission Requirements</p>

<hr>
<p>We have dependecies as well. This module as of now depends on the AI Module, Gemini Provider Module as well as the Key Module for managing all the keys. You have to install and enable all these modules for the AI Recipe Generator to function. This is actually cool as we are using the Drupal Ecosystem to do our tasks. </p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780559026/drupal/dlej7qami6d1p4xzcqjv.png" alt="Flow Diagram" /><figcaption>Flow Diagram</figcaption></figure>
<h3>Phase 1: The Setup (Settings Form)</h3>
<p>Before a user can generate a recipe, an administrator sets the rules.</p>
<ol>
<li><strong>Setting the Ground Rules:</strong> An admin visits the &quot;AI Recipe Generator Settings&quot; form and enters the <strong>System Instructions</strong> (e.g., &quot;You are a senior Drupal developer...&quot;).</li>
<li><strong>Saving to the Database:</strong> When the admin clicks &quot;Save configuration,&quot; Drupal uses its configuration service (<code>@config.factory</code>) to safely store these instructions in the database for later use.</li>
</ol>
<h3>Phase 2: The User Request (Test UI Form)</h3>
<p>This is where the actual generation happens.</p>
<ol start="3">
<li><strong>User Input:</strong> A user opens the &quot;AI Recipe Generator (Test UI)&quot; form. They select their preferred <strong>AI Provider</strong> (like Gemini) and type exactly what they want to build into the <strong>Recipe Prompt</strong> box.</li>
<li><strong>Triggering the AJAX Call:</strong> The user clicks the <strong>Generate Recipe</strong> button. Instead of reloading the whole webpage, the browser sends a quiet, background message (an <strong>AJAX Request</strong>) to the Drupal server containing the user&#39;s prompt and their chosen AI provider.</li>
</ol>
<h3>Phase 3: The Backend Brain (Drupal Services)</h3>
<p>Once Drupal receives the AJAX request, it hands the work over to the custom services defined in your <code>.yml</code> file.</p>
<ol start="5">
<li><strong>Gathering the Pieces:</strong> The main service (<code>RecipeGenerationService</code>) goes to work collecting three crucial pieces of information:</li>
</ol>
<ul>
<li><strong>The Rules:</strong> It pulls the saved &quot;System Instructions&quot; using <code>@config.factory</code>.</li>
<li><strong>The Context:</strong> It asks the <code>ContextExtractor</code> service to pull any relevant background information about your specific Drupal site.</li>
<li><strong>The Request:</strong> It takes the user&#39;s specific &quot;Recipe Prompt&quot; from the AJAX call.</li>
</ul>
<ol start="6">
<li><strong>Talking to the AI:</strong> The <code>RecipeGenerationService</code> combines the rules, the context, and the user&#39;s request into one massive prompt. It then uses the <code>@ai.provider</code> service to send this combined package over the internet to the external AI (e.g., Gemini).</li>
</ol>
<h3>Phase 4: The Result (AJAX Response)</h3>
<ol start="7">
<li><strong>Receiving the Recipe:</strong> The external AI processes the prompt, generates the Starshot Recipe YAML file, and sends it back to your Drupal server.</li>
<li><strong>Updating the Screen:</strong> Drupal packages this generated recipe into an <strong>AJAX Response</strong> and sends it back to the user&#39;s browser. The browser reads this response and instantly displays the new recipe on the screen, completing the process without ever forcing the user to refresh the page.</li>
</ol>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780575149/drupal/jc0s5ilgchhaxo1kvveq.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Installing Drupal - Why its one of the most easiest installation]]></title>
            <description><![CDATA[A step-by-step technical guide on how to easily install and set up a Drupal project using Composer and DDEV for local development and module integration.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a21075f8d7f810de39a3084</link>
            <guid isPermaLink="false">6a21075f8d7f810de39a3084</guid>
            <pubDate>Thu, 04 Jun 2026 05:04:31 GMT</pubDate>
            <content:encoded><![CDATA[<p>Installing Drupal can be a task or it can be easy based on how you go about it. This is my guide on  how I set up Drupal Core for module integrations and product development.</p>

<h2> How I set up a Drupal Project</h2><p>This is the bare minimum as it allows us to TEST &amp; SEE the progress in real time. But setting this up is often challenging for many of us. Especially venturing into first time integrations. I have broken it down step by step so that it becomes easier for you. </p>
<p>You might but AI for the steps but trust me it may guide you wrong 😭</p>
<blockquote>
<p>You will need ddev &amp; git installed on your computer</p>
</blockquote>
<p><strong>Step 1: Creating a project</strong></p>
<p>Just be this step we can set up a sample project without any configurations. Here intead of <em>drupal_sandbox</em>, your name can be anything. Literally anything!</p>

<pre><code class="language-bash">composer create-project drupal/recommended-project drupal_sandbox
cd drupal_sandbox</code></pre>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780551130/drupal/dgddptherobvlfafedkr.png" alt="Ouput" /><figcaption>Output should look like this</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780551175/drupal/eqj0ppysns3pu6db9tvt.png" alt="End of the process" /><figcaption>End should look something like this</figcaption></figure>
<p><strong>Step 2: Making the ddev config file</strong></p>
<p>This file is responsible for handling your environment under the hood. Press ENTER after running this command. It takes care of the following:</p>
<p><strong>1) The Engine Type (type):</strong> Tells the system what framework you are using (like Drupal 11). DDEV instantly optimizes the Nginx web server and PHP settings specifically for that framework&#39;s unique code structure.</p>
<p><strong>2) The Front Door (docroot):</strong> Defines the exact folder that is exposed to the internet (like the web/ folder). It acts as a security barrier, blocking public access to your raw backend code, configuration files, and third-party dependencies.</p>
<p><strong>3) The Toolbox (php_version, db_type):</strong> Locks in the exact version numbers for your programming language, database engine, and runtime tools so your local server perfectly matches your production server.</p>
<p><strong>4) The Autopilot (hooks):</strong> Contains a list of custom terminal commands that you want executed automatically at specific moments—like running database migrations or flushing caches right after the containers turn on.</p>

<pre><code class="language-bash">ddev config</code></pre>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780551824/drupal/iiq9f2voap9fwene2gy5.png" alt="Output " /><figcaption>Output after running command</figcaption></figure>
<h2>Step 3: Start it</h2><p>Now you should run ddev start. Chances are it might throw an error :(</p>

<pre><code class="language-bash">ddev start</code></pre>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780552034/drupal/gw9iyd8k6ccrea3r6qic.png" alt="Output" /><figcaption>After Running ddev start</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780552079/drupal/hgr8devtyxregfim1nwm.png" alt="Error" /><figcaption>Error Message</figcaption></figure>
<p>This typically happens due to port mapping issue. Now a quick fix around this is that you can change the port mapping in the .ddev/config.yml file at the line number 47 &amp; 48. Update it with these lines and then do the restart.</p>

<pre><code class="language-yaml">router_http_port: 8080  
router_https_port: 8443</code></pre>
<pre><code class="language-bash">ddev restart</code></pre>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780552492/drupal/kpt9jaty0fulipaxblig.png" alt="Drupal image" /></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780552529/drupal/l3aecpjlph0e9ppyx8s4.png" alt="Now it will run 🎊🎊" /><figcaption>Now it will run 🎊🎊</figcaption></figure>
<h2>Step 4: Specifications Selection</h2><p>Select Save &amp; Continue for most of it. Now when prompted for the email, username &amp; password. Type something that you remember. I usually type: admin (for username &amp; password)</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780552807/drupal/wv2c97jt58ubjahekpc3.png" alt="Important step" /></figure>
<h1>Congrats!</h1><p>You have finally installed Drupal! Now you can start contributing here as well. </p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780553732/drupal/c0ykii4w1srdt3pysglp.png" alt="Into Drupal" /></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780553754/drupal/uolna9jhqvwaznvsa0zb.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PIRLS - Drupal's Core .yml files]]></title>
            <description><![CDATA[Learn about Drupal's core .yml files, including info, routing, and services, explained simply using a restaurant analogy to help you understand module configuration.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a200d7d5f3958f4a28782d7</link>
            <guid isPermaLink="false">6a200d7d5f3958f4a28782d7</guid>
            <pubDate>Wed, 03 Jun 2026 11:18:21 GMT</pubDate>
            <content:encoded><![CDATA[<p>If you have opened some repository, chances are that you have worked on some of them! Now they are a lot, let us learn them in a simple way one by one.</p>
<p>Say you are a restraunt owner, starting you the amazing food chain called - &quot;More Food, Less Money&quot;. You attract a lot of hungry customers who want quality food. So to start and scale up, you have to do the following things</p>
<ol>
<li><p>Get your Business License.</p>
</li>
<li><p>Decide on the Host &amp; Menu. </p>
</li>
<li><p>Bing new staff &amp; Kitchen Appliances</p>
</li>
<li><p>Set up good Decor &amp; Ambiance. <em>Ik you love an asthetic place ;)</em></p>
</li>
<li><p>Establish for a keycard system for security.</p>
</li>
</ol>

<blockquote>
<p>Here let&#39;s call it ai_chef the module</p>
</blockquote>

<h2>The Business Liscence -  [module].info.yml</h2><p>Before you can open, you need a license. This tells the city (Drupal) your restaurant&#39;s name, its description, and what basic utilities (dependencies) it requires to function. Without this, the city doesn&#39;t even know your restaurant exists.</p>

<pre><code class="language-yaml">name: &#39;AI Vegetarian Recipe Generator&#39;
type: module
description: &#39;Integrates with the Gemini API to generate custom vegetarian recipes.&#39;
core_version_requirement: ^10 || ^11
package: Custom AI
dependencies:
  - drupal:key</code></pre>
<h2>The Host & Menu - [module].routing.yml</h2><p>When a customer asks for a table or a specific dish (a URL request like /about-us), the host uses this file to direct them to the exact right table or kitchen station (the PHP Controller) that will serve them.</p>

<pre><code class="language-yaml">ai_chef.menu_generator:
  path: &#39;/admin/kitchen/generate-recipe&#39;
  defaults:
    _controller: &#39;\Drupal\ai_chef\Controller\RecipeController::buildMenu&#39;
    _title: &#39;Generate New Recipes&#39;
  requirements:
    _permission: &#39;generate veg recipes&#39;</code></pre>
<h2>The Kitchen Staff & Appliances - [module].services.yml</h2><p>You don&#39;t buy a new oven every time someone orders a pizza. This file registers your heavy-duty, reusable tools (PHP classes) and connects them to the main power grid (Dependency Injection). It ensures your module can efficiently share resources with the rest of Drupal.</p>

<pre><code class="language-yaml">services:
  ai_chef.gemini_service:
    class: Drupal\ai_chef\Service\GeminiRecipeService
    arguments: [&#39;@http_client&#39;, &#39;@config.factory&#39;]</code></pre>
<h2>The Decor & Ambiance - [module].libraries.yml</h2><p>This manages your paint colors, lighting, and music (CSS and JavaScript). You don&#39;t want to pay for a mariachi band in the quiet dining room; this file lets you define groups of styles/scripts and load them only on the specific pages that need them.</p>

<pre><code class="language-yaml">kitchen_ambiance:
  version: 1.0
  css:
    theme:
      css/restaurant-theme.css: {}
  js:
    js/gemini-integration.js: {}
  dependencies:
    - core/drupal
    - core/jquery</code></pre>
<h2>The Keycard System - [module].permissions.yml</h2><p>This dictates security. It defines the specific digital locks on your doors (e.g., &quot;Access the VIP lounge&quot; or &quot;Edit the main menu&quot;) so the site administrator can hand out the right keycards to specific staff roles.</p>

<pre><code class="language-yaml">generate veg recipes:
  title: &#39;Generate Vegetarian Recipes&#39;
  description: &#39;Allows staff to use the Gemini API to create new menu items.&#39;
  restrict access: true</code></pre>
<h1>A cool way to remember this?</h1><p>You struggle to remember all these things as this is a lot of juggling for you, :(</p>
<p>But don&#39;t worry, your best friend suggests you something to help you:</p>
<p>To remember the five main .yml files, think of stringing together a necklace of PIRLS (Pearls).</p>
<p>P - Permissions ([module].permissions.yml)</p>
<p>I - Info ([module].info.yml)</p>
<p>R - Routing ([module].routing.yml)</p>
<p>L - Libraries ([module].libraries.yml)</p>
<p>S - Services ([module].services.yml)</p>

<p><em>Some sources</em></p>
<p><a href="drupal.org/docs/develop/creating-modules">Main Module Development Guide</a></p>
<p><a href="drupal.org/node/2000204">Understanding info.yml</a></p>
<p><a href="drupal.org/docs/drupal-apis/routing-system">Routing &amp; Controllers (routing.yml)</a></p>
<p><a href="drupal.org/docs/drupal-apis/services-and-dependency-injection">Services &amp; Dependency Injection (services.yml)</a></p>
<p><a href="drupal.org/docs/drupal-apis/javascript-api/javascript-api-overview">Asset Management (libraries.yml)</a></p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780488980/drupal/zqiai6xphrhqcufleqgt.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding your first piece of code to Drupal Project!]]></title>
            <description><![CDATA[Learn how to set up your Drupal project codebase, initialize a Git repository, and push your first commit following Drupal's version control guidelines.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a1bbbde588c949600e9725f</link>
            <guid isPermaLink="false">6a1bbbde588c949600e9725f</guid>
            <pubDate>Sun, 31 May 2026 04:41:02 GMT</pubDate>
            <content:encoded><![CDATA[<p>Setting up the codebase on Drupal is <strong>EASY!</strong> Only you need a little bit of patience. Let&#39;s dive deep into it.</p>
<p>So previously we had set up a project. Now we are going to initialise the git repo and add a few changes. First if you have created your project, you can move on into the Version Control section. This will contain instructions of what we should do further. </p>
<p>This was my link: <a href="https://www.drupal.org/project/ai_recipe_generator/git-instructions">Version Control</a></p>

<figure><iframe src="https://www.youtube.com/embed/Ai0n_9vG_Ig" title="YouTube video" allowfullscreen></iframe></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780320368/drupal/cckksop4jlggh3e2mfvz.png" alt="Version Control " /><figcaption>Version Control</figcaption></figure>
<pre><code class="language-bash">mkdir ai_recipe_generator

cd ai_recipe_generator

git init

git checkout -b 1.0.x

echo &quot;AI Recipe Generator&quot; &gt; README.txt

git add README.txt

git commit -m &quot;Initial commit.&quot;

git remote add origin git@git.drupal.org:project/ai_recipe_generator.git

git push origin 1.0.x</code></pre>
<p>They are the explicit instructions of what we are going to do.</p>
<blockquote>
<p>Copying without understanding is useless. Let&#39;s go through each step one by one. </p>
</blockquote>

<table>
<thead>
<tr>
<th>Command</th>
<th>What it does</th>
<th>Why we do it</th>
</tr>
</thead>
<tbody><tr>
<td><strong><code>mkdir ai_recipe_generator</code></strong></td>
<td>Creates a new directory named <code>ai_recipe_generator</code>.</td>
<td>Allocates a dedicated local space for your project files.</td>
</tr>
<tr>
<td><strong><code>cd ai_recipe_generator</code></strong></td>
<td>Moves your terminal path into the new folder.</td>
<td>Ensures all future commands execute inside the project context.</td>
</tr>
<tr>
<td><strong><code>git init</code></strong></td>
<td>Initializes a hidden local <code>.git</code> repository.</td>
<td>Turns the plain folder into a version-controlled project.</td>
</tr>
<tr>
<td><strong><code>git checkout -b 1.0.x</code></strong></td>
<td>Creates and switches to a new branch named <code>1.0.x</code>.</td>
<td>Bypasses standard <code>main</code>/<code>master</code> to match Drupal&#39;s release branch rules.</td>
</tr>
<tr>
<td><strong><code>echo &quot;AI...&quot; &gt; README.txt</code></strong></td>
<td>Generates a <code>README.txt</code> file with project text.</td>
<td>Creates an initial asset to track, as Git cannot track empty folders.</td>
</tr>
<tr>
<td><strong><code>git add README.txt</code></strong></td>
<td>Stages the file to the Git index.</td>
<td>Explicitly marks the new file to be included in the next snapshot.</td>
</tr>
<tr>
<td><strong><code>git commit -m &quot;Text&quot;</code></strong></td>
<td>Saves the staged file with a historic log message.</td>
<td>Establishes the official baseline/first snapshot of your codebase.</td>
</tr>
<tr>
<td><strong><code>git remote add origin &lt;url&gt;</code></strong></td>
<td>Links your local Git setup to the Drupal.org repository.</td>
<td>Establishes the secure target destination path on Drupal&#39;s servers.</td>
</tr>
<tr>
<td><strong><code>git push origin 1.0.x</code></strong></td>
<td>Uploads the local branch and commit to the server.</td>
<td>Publishes your code online so your mentors can access and review it.</td>
</tr>
</tbody></table>

<p>Post this we had committed 3 more files of out own, which are equally crucial for the module development. </p>
<p><strong>1 ai_recipe_generator.module.info</strong></p>
<p><strong>2 composer.json</strong></p>
<p><strong>3 .gitignore</strong></p>
<p><em>Resources</em></p>
<ol>
<li>Module Creation &amp; .info.yml Documentation
<a href="https://www.drupal.org/docs/develop/creating-modules">Creating Custom Modules (Main Hub):</a> This is the central landing page for all module development tutorials in modern Drupal, covering everything from routing to creating custom blocks.</li>
</ol>
<p><a href="https://www.drupal.org/docs/develop/creating-modules/let-drupal-know-about-your-module-with-an-infoyml-file">Defining the .info.yml File:</a> This page provides the exact specifications, required properties, and core version requirement syntax for your module&#39;s metadata.</p>
<ol start="2">
<li>Composer Documentation for Modules
<a href="https://www.drupal.org/docs/develop/using-composer/add-a-composerjson-file">Adding a composer.json File to a Module:</a> This explains exactly why and how to add a composer.json file to your custom or contributed module, including the composer init steps specifically tailored for Drupal package types.</li>
</ol>

<pre><code class="language-yaml">name: AI Recipe Generator
description: Creates recipes using AI.
package: AI

type: module
core_version_requirement: ^10.5 || ^11.2

dependencies:
  - ai:ai</code></pre>
<pre><code class="language-json">{
    &quot;name&quot;: &quot;drupal/ai_recipe_generator&quot;,
    &quot;description&quot;: &quot;Creates recipes using AI.&quot;,
    &quot;type&quot;: &quot;drupal-module&quot;,
    &quot;license&quot;: &quot;GPL-2.0-or-later&quot;,
    &quot;authors&quot;: [
        {
            &quot;name&quot;: &quot;Hrishikesh Dalal&quot;,
            &quot;email&quot;: &quot;204620-hrishikesh-dalal@users.noreply.drupalcode.org&quot;
        }
    ],
    &quot;require&quot;: {}
}
</code></pre>
<h3>Somewhere where I struggled and made mistakes</h3><p>If you have seen the video, you would know I had made a mistake initializing the git repo 😅. I made a commit to the main brand before setting up the 1.0.x which is crucial for creating forks and deployments.  Well it sucked but it was a great source of learning. </p>
<p><strong>Understanding the Concept of Remote in Git</strong></p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780466304/drupal/ykvmgxf2ae8mky69ymg0.png" alt="Git Remote" /><figcaption>Git Remote</figcaption></figure>
<p>Think of Git <strong>Remotes</strong> as a <strong>Cloud Backup or a Shared Office.</strong></p>
<p>Imagine you are writing a book on your own private desk at home (<strong>Local Repository</strong>). You can write, delete, and organize pages however you want.</p>
<p>However, your publisher and co-authors are in a different building. To show them your work, you have a <strong>Shared Drafting Table</strong> (<strong>Remote Repository</strong>) in their office.</p>
<p><strong>1. A &quot;Remote&quot;:</strong> It&#39;s just a bookmark or a &quot;saved contact&quot; in your computer&#39;s address book that tells Git exactly where that Shared Table is located on the internet.</p>
<p><strong>2. Origin:</strong> This is just the default nickname for that Shared Table. (But you could call it anything, backup, publish etc)</p>

<p><strong>Managing Remotes: The Commands</strong></p>
<p>Sometimes you need to change where your code is going (e.g., commit to a different fork or repository).</p>
<p><strong>1. Check your current remotes</strong></p>
<p>Before changing anything, see where your project is currently &quot;talking&quot; to.</p>
<p><em>The <code>-v</code> stands for &quot;verbose.&quot; It shows you the URLs for fetching (downloading) and pushing (uploading).</em></p>

<pre><code class="language-bash">git remote -v</code></pre>
<p><strong>2. Change a remote&#39;s URL</strong></p>
<p>If the server location changes, but you want to keep the same nickname (<code>origin</code>), use this command:</p>

<pre><code class="language-bash">git remote set-url origin https://git.drupalcode.org/issue/ai_recipe_generator-3592994.git</code></pre>
<blockquote>
<p><strong>When to use:</strong> Use this if you accidentally typed the wrong URL during setup or if the project moved to a new server.</p>
</blockquote>

<p><strong>3. Add a second remote</strong></p>
<p>You can have more than one! You might have one for a private backup and one for the public Drupal project.</p>

<pre><code class="language-bash">git remote add backup https://git.drupalcode.org/issue/ai_recipe_generator-000001.git</code></pre>
<ol start="4">
<li>Rename a remote</li>
</ol>
<p>If you don&#39;t like the name <code>origin</code> and want to call it something else: (here drupal_server)</p>

<pre><code class="language-javascript">git remote rename origin drupal_server</code></pre>
<p><strong>5. Remove a remote</strong></p>
<p>If you want to cut ties with a specific server:</p>

<pre><code class="language-javascript">git remote remove backup
</code></pre>
<blockquote>
<p><strong>Pro-Tip:</strong> If you ever get an error saying &quot;Remote origin already exists,&quot; it means your project is already linked to a server. Use <code>set-url</code> to update it instead of <code>add</code>.</p>
</blockquote>

<h1>Eventually....</h1><p>So after taking a short detour, we finally pushed our initial code to gitlab! 🎉🎉</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780469509/drupal/uyzpyjdvdaginxkooibj.png" alt="Final Code Push" /><figcaption>Final Code Push</figcaption></figure>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating a Module Project on Drupal (NO CODE) (With BONUS Section)]]></title>
            <description><![CDATA[Learn how to create a Drupal Module Project without writing any code. This guide covers the different project types on Drupal and the step-by-step creation process, perfect for GSoC beginners.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a1bbb83a9efe6ea5bb97c16</link>
            <guid isPermaLink="false">6a1bbb83a9efe6ea5bb97c16</guid>
            <pubDate>Sun, 31 May 2026 04:39:31 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hey there!</p>
<p>Welcome to my GSoC journey where in I am going to take you through an adventure of events. This blog is for the Level 1. Or rather I should saying learning about how to &quot;make the dish&quot;. We are going to create our very own Drupal Module Project!! </p>
<p>I will also append a Youtube link for the same if it does suit you better. Lets get into it!</p>

<figure><iframe src="https://www.youtube.com/embed/z7AmnRiPOso" title="YouTube video" allowfullscreen></iframe></figure>
<h1>Step 1: Understanding what all types of Projects you can create</h1><p>When you head over to this site for project creation - <a href="https://www.drupal.org/docs/develop/managing-a-drupalorg-theme-module-or-distribution-project/creating-a-new-project/how-to-create-a-new-project">Drupal Project Create</a>. You will have 4 options. </p>
<p>Drupal offers a few different project types:</p>
<p><strong>1. Module project:</strong> Contains PHP code that extends Drupal or adds new features.</p>
<p><strong>2. Theme project:</strong> Changes the look and feel of a Drupal site.</p>
<p><strong>3. General project:</strong> Contains code, but isn&#39;t a module, theme, or specific type.</p>
<p><strong>4. Community project</strong>: Non-code projects used to manage groups or camps.</p>
<p>Since our aim is to build an AI Recipe Generator that adds brand-new functionality to the site, selecting the Module project was the clear choice.</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780307515/drupal/jny9ogiki6x5mddhw8kc.png" alt="Types of Drupal Projects" /><figcaption>Types of Drupal Projects</figcaption></figure>
<h1>Step 2: Have Git on your computer</h1><p>The absolute bare minimum as all the code changes everything should be done by this. But if you reading this and are a techie, you must already be having git : )</p>

<h1>Step 3: Creating a project</h1><p>Now when you go over this link: <a href="https://www.drupal.org/project/add">Project Creation</a>, you will have a ton of options. Let us go through each one of them one by one. </p>
<ul>
<li><strong>Drupal core:</strong> The foundational software that runs the entire Drupal platform.</li>
<li><strong>Distribution project:</strong> An all-in-one starter kit that bundles Drupal with pre-selected features to quickly launch a specific type of website.</li>
<li><strong>Community project:</strong> An organizational space for managing Drupal groups, events, and people rather than website code.</li>
<li><strong>General project:</strong> A catch-all category for miscellaneous code, tools, and libraries that don&#39;t fit into the standard module or theme categories.</li>
<li><strong>Module project:</strong> A functional plugin that adds new features or changes how your website works.</li>
<li><strong>Theme project:</strong> The design package that controls the colors, layout, and overall visual appearance of your website.</li>
<li><strong>Theme Engine project:</strong> The behind-the-scenes system that translates your theme&#39;s visual design into something the Drupal platform can understand.</li>
<li><strong>Translation project:</strong> An outdated archive for language translations, which are now managed on a separate, dedicated website.</li>
</ul>

<h1>Step 4: The Nitty Gritties</h1>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780309249/drupal/xzmdvudsel6amte9tgpf.png" alt="All Changes" /><figcaption>All the things you can do in a snap</figcaption></figure>
<p>Now when you go to the next page, there is this huge form that you need to fill. Let me take you across each filed one by one.</p>
<h3>The Basic Identity</h3>
<p><strong>Name: AI Recipe Generator</strong></p>
<ul>
<li><strong>What it is:</strong> The project&#39;s human-readable title.</li>
<li><strong>Why I chose it:</strong> It clearly tells users exactly what the tool does right away.</li>
</ul>
<p><strong>Short name: <code>ai_recipe_generator</code></strong></p>
<ul>
<li><strong>What it is:</strong> The machine-readable system name used for URLs and codebases.</li>
<li><strong>Why I chose it:</strong> It matches the readable name using standard Drupal convention (lowercase and underscores) for consistency.</li>
</ul>
<p><strong>Project type: Full project</strong></p>
<ul>
<li><strong>What it is:</strong> Defines if it&#39;s a temporary &quot;Sandbox&quot; or an official &quot;Full project.&quot;</li>
<li><strong>Why I chose it:</strong> To get end-to-end control and official release packaging for this GSoC module.</li>
</ul>
<h3>Status and Classification</h3>
<p><strong>Maintenance status: Actively maintained</strong></p>
<ul>
<li><strong>What it is:</strong> Shows the community how much attention the project gets.</li>
<li><strong>Why I chose it:</strong> I will be actively monitoring, updating, and managing this code for the next 12 weeks and beyond.</li>
</ul>
<p><strong>Development status: Under active development</strong></p>
<ul>
<li><strong>What it is:</strong> Indicates the codebase&#39;s current maturity.</li>
<li><strong>Why I chose it:</strong> The module is currently being built from the ground up and evolving daily.</li>
</ul>
<p><strong>Module categories &amp; Ecosystem: Artificial Intelligence (AI)</strong></p>
<ul>
<li><strong>What it is:</strong> Tags used to organize and filter Drupal projects.</li>
<li><strong>Why I chose it:</strong> Since the module relies on generative LLMs, this ensures the right audience finds it.</li>
</ul>
<p><strong>Dependencies: AI module</strong></p>
<ul>
<li><strong>What it is:</strong> Other modules required for this to work.</li>
<li><strong>Why I chose it:</strong> We are leveraging the existing Drupal AI core module to securely handle API configurations and process prompts.</li>
</ul>
<h3>Issue Tracking and Releases</h3>
<p><strong>Components: Code, Documentation, Miscellaneous, User interface</strong></p>
<ul>
<li><strong>What it is:</strong> Categories for issue queue tickets.</li>
<li><strong>Why I chose it:</strong> Granular tags keep the project organized and make bug triage much easier.</li>
</ul>
<p><strong>Enable issue tracker: Checked</strong></p>
<ul>
<li><strong>What it is:</strong> Allows the community to report bugs and submit patches.</li>
<li><strong>Why I chose it:</strong> Open source thrives on collaboration, and this is essential for community contributions.</li>
</ul>
<p><strong>Version options: Tagged releases, Development branch releases</strong></p>
<ul>
<li><strong>What it is:</strong> How Drupal packages the code for download via Composer.</li>
<li><strong>Why I chose it:</strong> It gives users the choice between a stable &quot;Tagged&quot; release or the bleeding-edge &quot;Development&quot; version.</li>
</ul>

<p><em>The Description that I put is below.</em></p>

<pre><code class="language-html">&lt;p&gt;The AI Recipe Generator is a module designed to accelerate site building by utilizing Artificial Intelligence to generate Drupal Recipes dynamically.&lt;/p&gt;

&lt;p&gt;By integrating with the Drupal AI ecosystem, this tool allows developers and site builders to input natural language prompts and automatically generate the necessary configuration, module dependencies, and structure required for a functional Drupal Recipe.&lt;/p&gt;

&lt;h3 id=&quot;module-project--features&quot;&gt;Features&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Integrates directly with the Drupal AI module to process prompts.&lt;/li&gt;
  &lt;li&gt;Dynamically generates &lt;code&gt;recipe.yml&lt;/code&gt; files and associated configurations.&lt;/li&gt;
  &lt;li&gt;Streamlines the creation of standard site features (e.g., blog setups, event content types) without manual YAML writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;module-project--post-installation&quot;&gt;Post-Installation&lt;/h3&gt;
&lt;p&gt;Once installed, you will configure the generator through the AI module&#39;s interface. You must have an active AI provider configured via the core AI module to process prompts and begin generating recipes.&lt;/p&gt;

&lt;h3 id=&quot;module-project--additional-requirements&quot;&gt;Additional Requirements&lt;/h3&gt;
&lt;p&gt;This module requires the following dependencies:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;AI (Artificial Intelligence) module ecosystem&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;module-project--recommended-libraries&quot;&gt;Recommended modules/libraries&lt;/h3&gt;
&lt;p&gt;There are currently no additional recommended modules beyond the required AI ecosystem dependencies.&lt;/p&gt;

&lt;h3 id=&quot;module-project--similar-projects&quot;&gt;Similar projects&lt;/h3&gt;
&lt;p&gt;While standard Drupal site-building tools exist, this project uniquely leverages generative LLMs and the new Drupal Recipe API to architect configurations via natural language.&lt;/p&gt;

&lt;h3 id=&quot;module-project--support&quot;&gt;Supporting this Module&lt;/h3&gt;
&lt;p&gt;This project is currently being developed as part of &lt;strong&gt;Google Summer of Code 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;module-project--community-documentation&quot;&gt;Community Documentation&lt;/h3&gt;
&lt;p&gt;Weekly progress, architectural breakdowns, and documentation for this GSoC 2026 project are being documented under the &lt;em&gt;Engineering in the Wild&lt;/em&gt; series.&lt;/p&gt;</code></pre>
<p>Further you can also add your logo here. It was also confusing for me to find out where the logo is. </p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780309200/drupal/rikmtxaeuoz4fwy1qz68.png" alt="Where to put Drupal Logo" /><figcaption>Where to put Drupal Logo</figcaption></figure>
<p>You are almost done!!! You can review and see the details what they are. And then finally you can save and your project is successfully created! You can view my module here:<a href="https://www.drupal.org/project/ai_recipe_generator">AI Recipe Generator</a></p>

<h1>Bonus: How to Add Maintainers</h1><p>Most probably, you will be working with someone else. And it will be necessary to add someone there. Head over to the Maintainers tab and select whom you want to add in the search box. You can select the necessary changes to be made and then press enter!</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780309656/drupal/ydzkopdllpf4rjglsgyq.png" alt="For adding Maintainers" /><figcaption>For adding Maintainers</figcaption></figure>
<blockquote>
<p>Follow Along for my next journey</p>
</blockquote>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I got into Google Summer Of Code - The Recipe.]]></title>
            <description><![CDATA[A reflective guide on how to approach Google Summer of Code, presented as a recipe for success focusing on communication, consistency, and choosing the right project.]]></description>
            <link>https://blog.hrishikeshdalal.tech/drupal/drupal/6a12f76bfb799b375fde7a77</link>
            <guid isPermaLink="false">6a12f76bfb799b375fde7a77</guid>
            <pubDate>Sun, 24 May 2026 13:04:43 GMT</pubDate>
            <content:encoded><![CDATA[<blockquote>Since my project is called AI Recipe Generator (not the food one though). This blog is in a recipe format : )</blockquote>
<p>We all have been there where Google Summer of Code (GSoC) looks daunting to start, feeling its a tall mountain, difficult to climb. It looks like a challenge. Trust me I have been there. But in reality is just getting started and maintaining consistency. Let&#39;s now prep the kitchen!</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780643149/drupal/clhycc3itdk7bmlgv7d8.png" alt="Put some image here" /><figcaption>It is worth it.</figcaption></figure>
<h1>The Spec Sheet</h1><ul>
<li><strong>Prep Time:</strong> 2.5 Months (On &amp; Off)</li>
<li><strong>Cook Time:</strong> 12 Weeks</li>
<li><strong>Servings:</strong> Mainly you!</li>
<li><strong>Difficulty:</strong> Easy (if you use the best ingredients!)</li>
</ul>

<h1>Ingredients Needed - 3C's</h1><p>1). <strong>Communication</strong>: The most important one. During the global GSoC welcome session, the organizers shared a powerful truth: <strong>our selection was 75% communication and only 25% coding</strong></p>
<p>2). <strong>Consistency</strong>: If you are regular, it is a point that people will see you will to collaborate. <em>(Although there is a work around this too)</em></p>
<p>3). <strong>Choosing</strong>: Make sure you choose the right repo &amp; right tech stack that fits you!</p>

<h1>Finding the right ingredients</h1><p>The most difficult part in GSoC is I believe finding out which repo to contribute to rather than the actual contribution. Choosing between your* time, tech stack and project complexity*, it a task. Even I spent quite a lot of time on deciding which one to actually to contribute too. </p>
<p>Had used this site: <a href="https://www.gsocorganizations.dev/">GSOC Organisation Site</a>. It <strong>helped me filter</strong> according to my tech stack (which was JS) and finally get to the repos. I had initially joined quite a few organisations as I had no clue how to go about it and shortlist it : )</p>

<p>I just got started, had not thought about the end output. What lingers when you start is where the easiest path forward is. Had tried RocketChat, and other repos where the tech stack was JS and available on github. </p>

<h2>Why did I stick with Drupal?</h2><p>COMMUNITY SUPPORT. There is a huge community there to help you if you get stuck. Somewhere down the line this made the difference which resulted in me never setting up another codebase of an organisation. Mind you, Drupal did have a steep learning curve, its not that easy to set up. But it worked in my favour once I set it up. Had posted a couple of doubts which truly helped me</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1779629489/drupal/lkanrhxuihezfsam69no.png" alt="Slack Msg" /><figcaption>My first message on slack</figcaption></figure>
<p><strong>You are going to get stuck! Work around it</strong></p>
<p>I believed in this and whenever I got stuck I used to put a message in the group and continue. Sure it felt uncertain but the community response put me right path.</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1779629642/drupal/ixoe0grqakcezf9kf7zf.png" alt="Slack Msg" /><figcaption>Getting stuck on issues</figcaption></figure>
<h1>Important Learning</h1><p>Before we move on to the rest of the journey, let me tell you one of the most important learning. <em>If you approach GSoC as a competition you are going to be anxious and always in a mindset - The other person has done more issues, he/she is better than me I should do more.</em></p>
<blockquote>
<p>A small change in mindset: How can I contribute more? How can I assist more? This is what even the mentors and org admins look for</p>
</blockquote>

<h1>My RECIPE for GSoC</h1><h2>Step 1: Interact with Mentors & Community Early On</h2><p>Get in touch with your Mentor/s. They are going to guide you throughout your journey. And keep interacting with the community! In the GSoC welcome session with all contributors across all organisation, the organisers mentioned a point - if was 75% Communication, 25% Coding. That got you selected. This is the one of the most important part of your GSoC journey. </p>
<p>I had probably troubled my mentor a lot - asked a lot of questions. But he was also happy to guide me! </p>
<h2>Step 2: Contribution & Documentation</h2><p>In the organisation, contribute to the various issues. It will serve two purposes - making yourself familiar with the codebase &amp; a record to you name. This will come in handy when the mentor is deciding which candidate to choose. Your PR might not be accepted, or you might have some bug in your code (happened a lot with me), it&#39;s alright, put comments on issue / slack. If the issue isn&#39;t accepted atleast you got engagement &amp; interaction!</p>
<p><em>Document your progress.</em> This is a thing which worked for me &amp; a few people in Drupal had also done this. I had written a blog on Medium: <a href="https://medium.com/@1010hrishikesh/markdown-paradox-c5da304d3735">Markdown Paradox</a>. This was for an R&amp;D task in my project description for which I wrote a blog as well as documented in word which I had shared with my mentor. A few others also had made blogs / a website for the same. </p>
<p>Bonus: You can add this in your proposal!</p>

<pre><code class="language-plaintext">// What I put in my proposal

1 Personal Information
2 GSOC Information
3 Project Information
4 Abstract
5 Problem Statement
6 Technical Architecture &amp; Implementation
  6.1 Validating &amp; Caching
  6.2 Sample Implementation Code
7 Risk Analysis &amp; Edge Cases
8 Detailed Timeline
9 Testing
10 Project Impact
11 Deliverables
12 Research Tasks
13 About Me &amp; Community Experience</code></pre>
<h2>Step 3: Problem Statement & MVP</h2><p>You should have a thorough understanding about the Problem Statement that you are picking to solve! It is extremely important that you solve it. Discuss on it and share your thoughts &amp; ideas with your mentor. You could start with the R&amp;D tasks mentioned and move ahead!</p>
<p>**MVP will be the star of the show! ** It showcases your idea of the problem statement and your technical prowess. It gives you a lot of brownie points as you have understood the implementation and what it takes to make a good project. I had also made an MVP and given it two voice-overs as an added bonus.</p>
<p>My MVP: <a href="https://github.com/Hrishikesh-Dalal/drupal-ai-recipe-generator">https://github.com/Hrishikesh-Dalal/drupal-ai-recipe-generator</a></p>
<h2>Step 4: Proposal Writing</h2><p>This is really important if not the most important. This is your main criteria for judgement. What are your views on the project. Be sure to include architecture diagram, the drawbacks, migitations testing strategies etc.</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1779635425/drupal/otxtqh4x0ptqg00a4ngb.png" alt="System Diagram" /><figcaption>My System Diagram</figcaption></figure>
<h1>Conclusion</h1><p>Felt really amazing when i finally made it into GSoC. Happy to be there!</p>

<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1779636662/drupal/aky7bkhozspzzrum0d2n.png" alt="Chat between my Mentor" /><figcaption>A short chat between my mentor</figcaption></figure>
<figure><img src="https://res.cloudinary.com/dz5i84j9k/image/upload/v1780644656/drupal/l2ogzyzt6lnbsedtwu4c.png" alt="Drupal image" /></figure>]]></content:encoded>
        </item>
    </channel>
</rss>