<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Richard on Medium]]></title>
        <description><![CDATA[Stories by Richard on Medium]]></description>
        <link>https://medium.com/@chensformers?source=rss-d80825e3e646------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*Ibcxqe4L6KkgMkf8.png</url>
            <title>Stories by Richard on Medium</title>
            <link>https://medium.com/@chensformers?source=rss-d80825e3e646------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 24 Aug 2026 00:51:28 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@chensformers/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[I Made My Lovable.dev Site SEO-Ready Without Paying for Prerender.io]]></title>
            <link>https://medium.com/@chensformers/i-made-my-lovable-dev-site-seo-ready-without-paying-for-prerender-io-7fa053a22121?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/7fa053a22121</guid>
            <category><![CDATA[lovable]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[seo]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[vercel]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Thu, 16 Apr 2026 05:05:37 GMT</pubDate>
            <atom:updated>2026-04-16T05:05:37.781Z</atom:updated>
            <content:encoded><![CDATA[<h3>How I kept Lovable’s beautiful design while shipping pre-rendered HTML to Google — using vite-react-ssg, GitHub, and Vercel.</h3><p>There’s a dirty secret about AI-built React sites: Google often can’t read them.</p><p>Lovable.dev is genuinely impressive. It generates beautiful, polished React UIs faster than any tool I’ve used. But when I checked Google Search Console after launching my site, the crawled page was essentially empty — just a &lt;div id=&quot;root&quot;&gt;&lt;/div&gt; shell. All my carefully crafted copy, headings, and feature descriptions were invisible to search engines.</p><p>The common fix people reach for is a third-party prerendering service like Prerender.io or Hado SEO. These sit in front of your site, detect bots, and serve cached HTML. They work — but they cost money, add latency, and introduce another service to maintain.</p><p>I found a cleaner path: <strong>Static Site Generation (SSG) directly in the build pipeline</strong>, using vite-react-ssg. No third-party service. No extra cost. Just real, pre-rendered HTML files served from Vercel&#39;s edge CDN.</p><p>Here’s exactly how I did it.</p><h3>Why Lovable Projects Don’t Work Well for SEO Out of the Box</h3><p>Lovable generates standard <strong>Vite + React Single Page Applications (SPAs)</strong>. In a SPA, the browser receives a nearly empty HTML file and then JavaScript builds the entire page after load. For users, this is fine — the experience is fast and interactive. For search engine crawlers, it’s a problem.</p><p>While Google <em>can</em> execute JavaScript, it’s a slower two-phase process. Your pages get indexed less reliably and often with a delay. More critically, other crawlers — AI agents, social media link previews, Bing, DuckDuckGo — frequently don’t run JavaScript at all. They see nothing.</p><p>Static Site Generation solves this by running your React components at <strong>build time</strong> and outputting real .html files for every route. When Google visits /pricing, it gets a fully formed HTML page — no JavaScript required to see your content.</p><p>The best part: the final output is just static files. No Node.js server. No runtime. Perfect for a marketing site, landing page, or documentation hub where all you really want is fast, indexable HTML anyway.</p><h3>The Architecture: A Three-Way Sync</h3><p>The setup I landed on looks like this:</p><pre>Lovable ⟷ GitHub ⟷ Vercel</pre><p>Crucially, every connection here is <strong>bidirectional</strong>:</p><ul><li><strong>Lovable ↔ GitHub</strong>: Lovable now supports two-way sync. Changes made in Lovable push to GitHub automatically. Changes made directly in GitHub (editing files, adding new ones) sync back into Lovable.</li><li><strong>GitHub → Vercel</strong>: Every push to GitHub triggers an automatic Vercel deployment via webhook. No manual steps required.</li></ul><p>This means I can keep designing in Lovable — which genuinely has better design taste than I do — while the build pipeline handles SSG transparently on every deploy.</p><h3>Step 1 — Install vite-react-ssg</h3><p>After connecting Lovable to GitHub and cloning the repo locally, install the SSG package:</p><pre>npm install vite-react-ssg</pre><p>Note: The popular vite-ssg package is Vue-specific. For React you need vite-react-ssg.</p><h3>Step 2 — Create src/routes.ts</h3><p>This is the file that tells vite-react-ssg which pages to pre-render at build time. Create it at src/routes.ts:</p><pre>export const routes = [<br>  { path: &#39;/&#39;,          lazy: () =&gt; import(&#39;./pages/Home.tsx&#39;) },<br>  { path: &#39;/pricing&#39;,   lazy: () =&gt; import(&#39;./pages/Pricing.tsx&#39;) },<br>  { path: &#39;/guides&#39;,    lazy: () =&gt; import(&#39;./pages/Guides.tsx&#39;) },<br>  { path: &#39;/about&#39;,     lazy: () =&gt; import(&#39;./pages/About.tsx&#39;) },<br>  { path: &#39;/contact&#39;,   lazy: () =&gt; import(&#39;./pages/Contact.tsx&#39;) },<br>]</pre><p>Every route listed here gets its own pre-rendered index.html in the build output. If a route isn&#39;t listed, it won&#39;t be pre-rendered — so make sure all your pages are included.</p><h3>Step 3 — Update src/main.tsx</h3><p>Replace the default SPA entry point with the SSG version:</p><pre>// Before — standard SPA<br>import { createRoot } from &#39;react-dom/client&#39;<br>import App from &#39;./App.tsx&#39;<br>import &#39;./index.css&#39;</pre><pre>createRoot(document.getElementById(&#39;root&#39;)!).render(&lt;App /&gt;)</pre><pre>// After — SSG enabled<br>import { ViteReactSSG } from &#39;vite-react-ssg&#39;<br>import { routes } from &#39;./routes&#39;<br>import &#39;./index.css&#39;</pre><pre>export const createRoot = ViteReactSSG({ routes })</pre><h3>Step 4 — Update package.json</h3><p>Change the build script from vite build to vite-react-ssg build:</p><pre>{<br>  &quot;scripts&quot;: {<br>    &quot;dev&quot;:   &quot;vite&quot;,<br>    &quot;build&quot;: &quot;vite-react-ssg build&quot;<br>  }<br>}</pre><h3>Step 5 — Guard Browser-Only Code</h3><p>SSG runs your components in Node.js at build time — there’s no window or document available. Lovable projects sometimes reference these directly. Guard them like this:</p><pre>// ❌ Crashes at build time<br>const width = window.innerWidth</pre><pre>// ✅ Safe<br>const width = typeof window !== &#39;undefined&#39; ? window.innerWidth : 0</pre><p>Anything inside useEffect() is already safe — it only runs in the browser, never during the SSG build.</p><h3>Step 6 — Create vercel.json at the Project Root</h3><p>This is a critical step that’s easy to get wrong. Create vercel.json in the root of the project:</p><pre>{<br>  &quot;cleanUrls&quot;: true<br>}</pre><p>cleanUrls: true tells Vercel to serve /pricing.html when someone visits /pricing, /guides/how-to-start-art-collecting.html for that route, and so on — automatically, without any rewrite rules.</p><p><strong>A common mistake here:</strong> many guides tell you to add a catch-all rewrite like { &quot;source&quot;: &quot;/(.*)&quot;, &quot;destination&quot;: &quot;/index.html&quot; }. Don&#39;t do this for SSG. That rule intercepts every request and serves the homepage HTML for everything — defeating the entire point of pre-rendering separate pages.</p><h3>Step 7 — Configure Vercel’s Build Command</h3><p>In Vercel’s project settings (or during initial import), set:</p><p>Setting Value Framework Preset Vite Build Command vite-react-ssg build Output Directory dist</p><p>That’s it. Vercel will run the SSG build on every deploy.</p><h3>How to Add Files Without Leaving GitHub</h3><p>Because Lovable ↔ GitHub sync is bidirectional, you can create src/routes.ts and vercel.json directly in GitHub&#39;s web editor. Lovable will automatically pick up those changes — no conflicts, no manual syncing.</p><p>This is useful for infrastructure files like vercel.json that Lovable doesn&#39;t need to &quot;know about&quot; but should still be in the repo.</p><h3>Verifying It Works</h3><p>After deploying, right-click your live page and select <strong>View Page Source</strong>. If SSG is working, you’ll see your actual content — headings, paragraphs, navigation links — directly in the HTML. You should also see data-server-rendered=&quot;true&quot; on the root div.</p><p>If you still see just &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;, the main.tsx change hasn&#39;t taken effect yet.</p><p>Your build output in dist/ should also contain individual HTML files per route:</p><pre>dist/<br>  index.html<br>  pricing.html<br>  about.html<br>  contact.html<br>  guides/<br>    how-to-start-art-collecting.html<br>    art-valuation.html</pre><p>Each one is a fully self-contained, pre-rendered page.</p><h3>What You Get at the End</h3><ul><li><strong>Real HTML for every page</strong> — crawlable by Google, Bing, AI agents, and social link previews</li><li><strong>No third-party prerendering service</strong> — no Prerender.io, no Hado SEO, no extra cost</li><li><strong>Lovable still does the design</strong> — the authoring workflow is unchanged</li><li><strong>Automatic deploys</strong> — push in Lovable or GitHub, Vercel rebuilds in under a minute</li><li><strong>Edge CDN delivery</strong> — Vercel serves static files from its global edge network</li></ul><p>The final architecture is deceptively simple: Lovable generates React, GitHub stores it, vite-react-ssg bakes it into HTML at build time, and Vercel serves it. No servers. No runtime. No prerender middleware.</p><p>Just fast, static, fully-indexed HTML — with a React developer experience that keeps your design looking great.</p><p><em>If you’re building on Lovable and care about SEO, this is the cleanest path I’ve found. The whole setup takes about an hour and pays dividends every time Google re-crawls your site.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7fa053a22121" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Step-by-step, 24-week Roadmap to Become An AI + FullStack Developer]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@chensformers/a-step-by-step-24-week-roadmap-to-become-an-ai-fullstack-developer-531c6fbc78db?source=rss-d80825e3e646------2"><img src="https://cdn-images-1.medium.com/max/2600/0*fonYwTL35g96Ji1d" width="5184"></a></p><p class="medium-feed-snippet">Here&#x2019;s a step-by-step roadmap (&#x2248;6 months) to master AI/LLMs and ship production-ready apps.</p><p class="medium-feed-link"><a href="https://medium.com/@chensformers/a-step-by-step-24-week-roadmap-to-become-an-ai-fullstack-developer-531c6fbc78db?source=rss-d80825e3e646------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@chensformers/a-step-by-step-24-week-roadmap-to-become-an-ai-fullstack-developer-531c6fbc78db?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/531c6fbc78db</guid>
            <category><![CDATA[full-stack-developer]]></category>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[machine-learning]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Wed, 20 Aug 2025 02:15:41 GMT</pubDate>
            <atom:updated>2025-08-20T02:15:41.791Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Understanding Asymmetric Property Visibility in PHP 8.4]]></title>
            <link>https://medium.com/@chensformers/understanding-asymmetric-property-visibility-in-php-8-4-10c96bc9b266?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/10c96bc9b266</guid>
            <category><![CDATA[php]]></category>
            <category><![CDATA[php-development]]></category>
            <category><![CDATA[php84]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Tue, 25 Mar 2025 23:16:49 GMT</pubDate>
            <atom:updated>2025-03-25T23:16:49.170Z</atom:updated>
            <content:encoded><![CDATA[<p>PHP 8.4, released in November 2024, introduces <strong>Asymmetric Property Visibility</strong>, which allows separate visibility levels for reading and writing class properties, enhancing access control while preserving encapsulation.</p><h3>What is Asymmetric Property Visibility?</h3><p>In <strong>PHP 8.4</strong>, you can now <strong>define different visibility levels for reading and writing a property</strong> within a class.</p><p>Previously, PHP allowed properties to be public, protected, or private, but <strong>both reading and writing had the same visibility level</strong>. With <strong>Asymmetric Property Visibility</strong>, you can set <strong>separate visibility for getting and setting a property</strong>.</p><h4>A Basic Example</h4><pre>class User {<br>    public string $name { public get; private set; }<br><br>    public function __construct(string $name) {<br>        $this-&gt;name = $name; // Allowed: private setter is used inside the class<br>    }<br>    public function changeName(string $newName) {<br>        $this-&gt;name = $newName; // Allowed: inside class<br>    }<br>}<br><br>$user = new User(&quot;Alice&quot;);<br>echo $user-&gt;name; // ✅ Allowed: public getter<br>$user-&gt;name = &quot;Bob&quot;; // ❌ Error: Cannot modify, setter is private</pre><h4>Key Takeaways</h4><ul><li>get visibility is <strong>public</strong>, allowing <strong>read access</strong> from anywhere.</li><li>set visibility is <strong>private</strong>, so <strong>only class methods</strong> can modify the property.</li></ul><h4>Example 2: Preventing Direct Modification of Sensitive Properties</h4><pre>class BankAccount {<br>    public int $balance { public get; private set; }\<br><br>    public function __construct(int $initialBalance) {<br>        $this-&gt;balance = $initialBalance;<br>    }<br>    public function deposit(int $amount) {<br>        $this-&gt;balance += $amount; // ✅ Allowed: inside class<br>    }<br>    public function withdraw(int $amount) {<br>        if ($amount &gt; $this-&gt;balance) {<br>            throw new Exception(&quot;Insufficient funds&quot;);<br>        }<br>        $this-&gt;balance -= $amount; // ✅ Allowed: inside class<br>    }<br>}<br>$account = new BankAccount(1000);<br>echo $account-&gt;balance; // ✅ Allowed: public getter<br>$account-&gt;balance = 500; // ❌ Error: Cannot modify, setter is private</pre><h4>Key Takeaways</h4><ul><li>Prevents <strong>unauthorized modifications</strong> to $balance while allowing <strong>controlled updates</strong> through class methods.</li><li><strong>Improves security</strong> by ensuring logic (e.g., preventing overdrafts) is <strong>always enforced</strong>.</li></ul><h3>Caveats &amp; Considerations</h3><ul><li>This feature <strong>only applies to </strong><a href="https://phpgrid.com/blog/typed-properties-in-php-7-4/"><strong>typed properties</strong></a> (int, string, array, etc.). Typed properties are supported since <a href="https://www.php.net/ChangeLog-7.php">PHP 7.4</a>.</li><li>You <strong>must</strong> specify a visibility level for <strong>both</strong> get and set (e.g., { public get; private set; }).</li><li>Asymmetric visibility <strong>cannot</strong> be applied to untyped properties.</li></ul><h3>How Was Asymmetric Property Visibility Achieved Before PHP 8.4?</h3><p>Before <strong>Asymmetric Property Visibility</strong>, PHP only allowed <strong>single visibility</strong> for properties (public, protected, or private). This meant that <strong>if a property was readable, it was also writable</strong> unless additional methods were used to restrict modifications.</p><p>To achieve similar behavior, developers had to use <strong>getter and setter methods</strong> manually using Getter &amp; Setter Methods (Manual Encapsulation)</p><p>For example:</p><pre>class User {<br>    private string $name;<br><br>    public function __construct(string $name) {<br>        $this-&gt;name = $name;<br>    }<br><br>    // Public getter method<br>    public function getName(): string {<br>        return $this-&gt;name;<br>    }<br><br>    // Private setter method (prevents external modification)<br>    private function setName(string $name): void {<br>        $this-&gt;name = $name;<br>    }<br><br>    public function changeName(string $newName): void {<br>        $this-&gt;setName($newName); // Allowed inside class<br>    }<br>}<br><br>$user = new User(&quot;Alice&quot;);<br><br>echo $user-&gt;getName(); // ✅ Allowed: public getter<br>$user-&gt;setName(&quot;Bob&quot;); // ❌ Error: Cannot access private method<br>$user-&gt;name = &quot;Bob&quot;; // ❌ Error: Cannot access private property</pre><h4>Problems with the Traditional Approach</h4><ol><li><strong>More Boilerplate Code</strong></li></ol><ul><li>Requires <strong>explicit getter and setter methods</strong> for each property.</li><li>Increases <strong>code duplication</strong> when dealing with many properties.</li></ul><p><strong>2. Less Readable &amp; Natural Code</strong></p><ul><li>Instead of $user-&gt;name, you need to call $user-&gt;getName().</li><li>Assigning a value ($user-&gt;name = &quot;Bob&quot;;) is replaced by a method ($user-&gt;changeName(&quot;Bob&quot;);).</li></ul><h3>Why Asymmetric Property Visibility Matters</h3><p>Asymmetric Property Visibility in PHP 8.4 enhances encapsulation by allowing developers to set different visibility levels for reading and writing a property. This means a property can be <strong>publicly readable but privately writable</strong>, preventing unintended modifications while still making data accessible.</p><p>Previously, achieving this required workarounds like getter and setter methods, but now it can be done more cleanly and intuitively. This feature improves code maintainability, security, and clarity, making it easier to enforce strict control over property access without extra boilerplate.</p><h3>Final Thoughts</h3><p>Before PHP 8.4, <strong>getter and setter methods were the only way</strong> to enforce asymmetric visibility. While effective, it resulted in <strong>verbose</strong> and <strong>less readable</strong> code. With <strong>Asymmetric Property Visibility</strong>, PHP now provides <strong>native support</strong> for defining different access levels for reading and writing properties, making <strong>property management much cleaner and more intuitive</strong>.</p><h3>About the Author</h3><p>The author is a full-stack web developer who created SQL-based data analytics and BI reports tool — Querro at (<a href="https://querro.io/">querro.io</a>).</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10c96bc9b266" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging PHP with VSCode and XDebug: A Step-by-Step Guide]]></title>
            <link>https://medium.com/@chensformers/debugging-php-with-vscode-and-xdebug-a-step-by-step-guide-c0380ca4e1a0?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/c0380ca4e1a0</guid>
            <category><![CDATA[mamp]]></category>
            <category><![CDATA[vscode]]></category>
            <category><![CDATA[xdebug]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[phpgrid]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Sun, 09 Mar 2025 06:22:30 GMT</pubDate>
            <atom:updated>2025-03-11T01:08:01.657Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/686/1*_yT5Uk5RHtP7gfu_EIICDw.jpeg" /></figure><p>Debugging is an essential part of PHP development, and using <strong>Visual Studio Code with XDebug</strong> can greatly enhance your workflow. This guide will walk you through setting up XDebug, enabling breakpoints, stepping through code, using stack traces, and troubleshooting common issues.</p><h3>1. Installing XDebug</h3><p>Before you can debug PHP with VSCode, you need to ensure that XDebug is installed on your system.</p><h4>Check if XDebug is Installed</h4><p>From a <a href="https://www.php.net/manual/en/function.phpinfo.php">phpinfo</a> page, and check wether XDebug section and it is enabled.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CEDmFLoAuUBu_PUortpazQ.png" /><figcaption>XDebug in phpinfo</figcaption></figure><h4>Install XDebug If It Is Missing</h4><p>If XDebug is missing, you can install it using <strong>pecl</strong>:</p><pre>pecl install xdebug</pre><p>After installation, add this line to your php.ini file:</p><pre>zend_extension=xdebug</pre><p>Remembere to restart your web server or PHP service:</p><pre>sudo systemctl restart apache2  # For Apache<br>sudo systemctl restart php-fpm  # For PHP-FPM</pre><p>Now we need to configure XDebug to connect to VSCode</p><h3>2. Configuring XDebug for VSCode</h3><p>Edit your php.ini file and add the following configuration:</p><pre>[xdebug]<br>xdebug.mode=debug<br>xdebug.start_with_request=yes<br>xdebug.client_host=127.0.0.1<br>xdebug.client_port=9003<br>xdebug.log=/var/log/xdebug.log</pre><h4>Important:</h4><p>Ensure your PHP service is restarted for any changes made in php.ini to take effect.</p><h3>3. Setting Up VSCode for XDebug</h3><ol><li><strong>Install the PHP Debug Extension</strong></li></ol><ul><li>Open VSCode.</li><li>Go to Extensions (Ctrl+Shift+X).</li><li>Search for <a href="https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug"><strong>PHP Debug</strong> by Felix Becker</a> and install it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EtKJrLxgKszYGJg_2eev7A.png" /><figcaption>PHP Debug Extension for VSCode</figcaption></figure><p><strong>2. Configure VSCode Debugger</strong></p><ul><li>Open your project in VSCode.</li><li>Go to the <strong>Run and Debug</strong> panel (Ctrl+Shift+D).</li><li>Click on <strong>create a launch.json file</strong>. It should create default configuration as the following with port 9003. It is absolutely critial to have the same port as what is in <strong>xdebug.client_port </strong>in php.ini in previous step.</li></ul><pre>{<br>   &quot;version&quot;: &quot;0.2.0&quot;,<br>    &quot;configurations&quot;: [<br>        {<br>            &quot;name&quot;: &quot;Listen for XDebug&quot;,<br>            &quot;type&quot;: &quot;php&quot;,<br>            &quot;request&quot;: &quot;launch&quot;,<br>            &quot;port&quot;: 9003<br>        },<br>        {<br>            &quot;name&quot;: &quot;Launch currently open script&quot;,<br>            &quot;type&quot;: &quot;php&quot;,<br>            &quot;request&quot;: &quot;launch&quot;,<br>            &quot;program&quot;: &quot;${file}&quot;,<br>            &quot;cwd&quot;: &quot;${fileDirname}&quot;,<br>            &quot;port&quot;: 9003<br>        }<br>    ]<br>}</pre><p><strong>3. Start the Debugger</strong></p><ul><li>Click the green <strong>Start Debugging</strong> button or press F5.</li><li>Ensure XDebug is running and listening on port <strong>9003. </strong>It actually can be any port number as 9003 is default.</li></ul><p>The debugger bar should now appear right below tabs in VSCode.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2XAD8pFwLrvdTlwIzJRvFg.png" /><figcaption>VSCode Debugger Bar</figcaption></figure><h3>4. Debugging Features</h3><h4>Setting Breakpoints</h4><ul><li>Open any PHP file.</li><li>Click in the left margin next to a line number or press F9 to set a breakpoint.</li><li>Run the debugger (F5) and execute the PHP script in your browser.</li><li>The script execution will pause at the breakpoint.</li></ul><h4>Stepping Through Code</h4><p>While debugging, you can control execution using:</p><ul><li><strong>Step Over (</strong><strong>F10)</strong> – Move to the next line of code.</li><li><strong>Step Into (</strong><strong>F11)</strong> – Enter into function calls.</li><li><strong>Step Out (</strong><strong>Shift+F11)</strong> – Exit the current function.</li><li><strong>Continue (</strong><strong>F5)</strong> – Resume execution until the next breakpoint.</li></ul><h4>Using Stack Trace</h4><ul><li>The <strong>Call Stack</strong> panel in VSCode shows the sequence of function calls leading up to the breakpoint.</li><li>Helps trace errors in deeply nested functions.</li><li>Click on any function in the stack to inspect its execution.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qTMd8upIxsuL__KengDHyg.png" /><figcaption>PHP XDebug breakpoint</figcaption></figure><h3>5. Troubleshooting Common Issues</h3><h4>Issue 1: Debugger Not Stopping at Breakpoints</h4><ul><li>Ensure XDebug is installed and configured correctly in phpinfo (See step 1)</li><li>Make sure the matching port (e.g. 9003) is set in both php.ini and launch.json.</li><li>Always restart Apache or PHP-FPM after making changes.</li></ul><h4>Issue 2: XDebug Not Connecting to VSCode</h4><ul><li>Verify the logs in /var/log/xdebug.log for connection issues.</li><li>Ensure VSCode is listening for XDebug (F5 in debug mode).</li><li>Disable other applications using port 9003 (like another debugger).</li></ul><h4>Issue 3: Path Mapping Problems</h4><ul><li>Ensure pathMappings in launch.json correctly maps the server path to the local workspace</li><li>Add $_SERVER[&#39;DOCUMENT_ROOT&#39;] in your script to verify paths.</li></ul><h3>Conclusion</h3><p>VSCode and XDebug together make debugging PHP a seamless experience. By following this guide, you should be able to install and configure XDebug properly, and set breakpoints and step through code, and also troubleshoot common debugging issues.</p><p>Finally, if manually tinkering isn’t your thing, you can opt for a hassle-free solution like <a href="https://www.mamp.info">MAMP</a> or <a href="https://www.apachefriends.org/">XAMMP</a>.</p><p>Happy debugging! 🚀</p><h3>About the Author</h3><p>The author is a full-stack web developer who created SQL-based data analytics and BI reports tool — Querro at (<a href="https://querro.io">querro.io</a>).</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c0380ca4e1a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Simple Hack to Learn 2X Faster]]></title>
            <link>https://medium.com/@chensformers/simple-hack-to-learn-2x-faster-659fe75db127?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/659fe75db127</guid>
            <category><![CDATA[learning-hacks]]></category>
            <category><![CDATA[2x-learning]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[life-hacking]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Sat, 28 Dec 2024 21:02:51 GMT</pubDate>
            <atom:updated>2024-12-31T08:31:25.670Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VlbdO-UWMhISlIYO" /><figcaption>Photo by <a href="https://unsplash.com/@mischievous_penguins?utm_source=medium&amp;utm_medium=referral">Casey Horner</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>While learning React, I couldn’t help but notice how slow the instructor spoke in the recorded lectures. It makes sense being intentionally slow to give time for students to think while absorbing the materials. But after a while, it started to feel like I was waiting for molasses to pour on a chilly day.</p><p>That’s when I discovered the magical speed-up button. Just like that, I was zipping through lectures at 2x speed. It’s like turning your study sessions into an action movie — everything’s faster, more intense, and oddly satisfying.</p><p>It turns out, this isn’t just a life hack — it’s backed by science! According to a <a href="https://www.caltech.edu/about/news/thinking-slowly-the-paradoxical-slowness-of-human-behavior">Caltech study</a>, <strong>the human brain is actually <em>faster</em> than our ears.</strong> “Multimodal learning” — fancy talk for combining audio and visuals — supercharges how much we can absorb. So, not only are you learning faster, but your brain is also giving you a virtual high-five for being efficient.</p><h3><strong>Why It Works</strong></h3><p>Our brains is capable to process and retain information much faster than the pace of normal speech. Increasing the playback speed while listening, it helps to</p><ul><li><strong>Increases Focus</strong>: The faster pace reduces the tendency to zone out.</li><li><strong>Improves Comprehension</strong>: Your brain adapts to processing information more efficiently over time.</li><li><strong>Saves Time</strong>: Completes a 2-hour lecture in just 1 hour, leaving room for review or practice.</li></ul><h3><strong>How to Use the Hack Effectively</strong></h3><p>It does take some practice to get to 2x speed-listening. For me, 1.2x is a good comfortable starter pace; 1.5x is efficient and manageable for most content; and 2.0x requires focus but significantly speeds up learning. With time and practice, anyone can adapt and effectively process content at faster speeds.</p><p>I’ve tried experimenting with speeds beyond 2x, but it quickly becomes overwhelming and impractical, often leading to burnout. I wouldn’t recommend it with only exception I’m reviewing previously learned material or revisiting old chapters.</p><p>In many video players, such as the one in <strong>Udemy</strong> and <strong>Coursea</strong>, you can simply press the “+” or “-” keys on your keyboard to adjust the playback speed while media is playing. It can often go up to 10x speed before becoming unrecognizable! It’s amazing to fly through previously learned materials using these keys. Speed up (+) as much as you can still retain information, and slow down (-) when you need to sink in the details.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KViTFu-AHqBuwwrVJzznVg.png" /><figcaption>Plus and Minus to speed up and down playback speed</figcaption></figure><p>1. <strong>Start Gradually</strong></p><p>Don’t expect you will be zipping through materials like superman onset. Instead, try at 1.25x or 1.5x speed to get used to faster playback. Gradually increase to 2x speed as your brain adapts to processing quicker speech.</p><p>2. <strong>Leverage Closed Captions</strong></p><p>This is super important as part of “multimodal learning” to engage our visual sensors to read subtitles to help with clarity, especially for technical or unfamiliar content.</p><p><strong>3. Take Notes Strategically</strong></p><p>Use digital tools like <strong>Notion</strong> or <strong>OneNote</strong> to jot down key points quickly. Online classes like Udemy and Coursea have note taking tools built-in with video timestamp.</p><p>4. <strong>Revisit Critical Sections</strong></p><p>It’s perfectly fine to slow down and walk after a sprint, especially when revisiting difficult sections at a normal speed to ensure thorough understanding.</p><h3><strong>Free Tools for Enhanced Learning</strong></h3><p>1. <strong>Playback Speed Controllers</strong>:</p><ul><li><strong>Chrome Extensions</strong>:</li><li><a href="https://chromewebstore.google.com/detail/video-speed-controller/nffaoalbilbmmfgbnbgppjihopabppdk"><strong>Video Speed Controller</strong></a>: Allows fine-tuned speed adjustments on most video platforms.</li><li><a href="https://chromewebstore.google.com/detail/enhancer-for-youtube/ponfpcnoihfmfllpaingbgckeeldkhle?hl=en-US"><strong>Enhancer for YouTube</strong></a>: Adds playback speed shortcuts for YouTube.</li><li><strong>Native Tools</strong>:</li><li>YouTube, Udemy, Coursera, and similar platforms often have built-in speed controls.</li></ul><p>2. <strong>Note-Taking Apps</strong>:</p><ul><li><a href="https://www.notion.com/"><strong>Notion</strong></a><strong>, </strong><a href="https://obsidian.md/"><strong>Obsidian</strong></a><strong>, or </strong><a href="https://evernote.com/"><strong>Evernote</strong></a>: Great for organizing notes with tags, links, and summaries.</li><li><a href="https://apps.apple.com/us/app/audionote/id369820957"><strong>AudioNote</strong></a>: Syncs audio or video with your notes.</li><li><a href="https://reclipped.com/"><strong>ReClipped</strong></a>: Lets you save video highlights and make notes directly linked to timestamps.</li></ul><p>3. <strong>Flashcard Tools</strong>:</p><ul><li><a href="https://apps.ankiweb.net/"><strong>Anki</strong></a> (spaced repetition): Use it to create cards from your notes for active recall.</li><li><a href="https://quizlet.com/"><strong>Quizlet</strong></a>: Easier to set up and includes premade decks.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IXgfO5CRMvrSshN0qDa08w.png" /><figcaption>Udemy course player with up to 2X play speed</figcaption></figure><blockquote>You can simply press the “+” or “-” keys on your keyboard to adjust the playback speed. It can often go up to 10x speed before becoming unrecognizable!</blockquote><h4><strong>Sample Routine for Learning with 2x Speed</strong></h4><p>I’ve developed an approach to studying at 2x speed that strikes a balance between efficiency and comprehension. My method combines strategic preparation, active note-taking, and consistent review, allowing me to absorb complex concepts quickly while ensuring a solid understanding.</p><p>Here’s how I’ve made it work for me and how it can work for you too.</p><ol><li><strong>Preparation (5 mins)</strong>:</li></ol><ul><li>Review the syllabus or key concepts for the session.</li><li>Set a specific goal (e.g., “Understand how recursion works”).</li></ul><p>2. <strong>Study (30–60 mins)</strong>:</p><ul><li>Watch the video at 1.2x — 2x speed, adjust accordingly</li><li>Pause to note down key points or rewatch unclear sections.</li></ul><p>3. <strong>Review (15 mins)</strong>:</p><ul><li>Summarize what you learned.</li><li>Optional — Create flashcards or a mind map of the main ideas.</li></ul><p>4. <strong>Practice (30+ mins)</strong>:</p><ul><li>Practice exercises or implement the learned concepts in real-world scenarios.</li></ul><h3>Final Thoughts</h3><p>This simple hack can transform how you learn, making it faster, more efficient, and more engaging. Combine it with proven techniques like active recall and spaced repetition, and you’ll be well on your way to mastering new skills in no time. Try it out, and unlock your potential!</p><h3>About the Author</h3><p>The author is a veteran web developer who created the popular PHP datagrid tool (<a href="https://phpgrid.com/">phpgrid.com</a>), harnessing the power of CRUD to make the world a better place — at least for developers looking to simplify their lives!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=659fe75db127" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Naming Variables Can Distinguish a Good Programmer from a Bad One]]></title>
            <link>https://medium.com/@chensformers/why-naming-variables-can-distinguish-a-good-programmer-from-a-bad-one-b6889a8c80aa?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/b6889a8c80aa</guid>
            <category><![CDATA[art-of-teaching]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[programming-tips]]></category>
            <category><![CDATA[variables]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Sat, 23 Nov 2024 19:05:11 GMT</pubDate>
            <atom:updated>2024-11-23T22:51:40.624Z</atom:updated>
            <content:encoded><![CDATA[<p>While analyzing some old code from a previous project, I stumbled upon variable names that left me scratching my head. Here’s a snippet of what I found:</p><pre>var type = 0;<br>...<br>var wfStart = &quot;Server Workflow Start&quot;;<br>var num = 100000;<br>...<br>var yesOrNo = &quot;Yes&quot;;</pre><p>At first glance, these names seemed cryptic at best. After 25 minutes of piecing together the context (aided by poorly written comments), I finally understood what they meant.</p><p>What’s wrong here? To me, it seems the original developer rushed through the implementation, disregarding the importance of meaningful variable names. Instead, they left behind a confusing mess for anyone working on the code later.</p><p>Imagine the difference if the variables were given descriptive, self-explanatory names. Here’s an improved version:</p><pre>var typeCode = 0;<br>const SERVER_WORKFLOW_START = &quot;Server Workflow Start&quot;;<br>var maxLimit = 100000;<br>var isConfirmed = true;</pre><p>These changes transform the code into something far more understandable. It reflects a clear understanding of the task at hand, good communication of intent.</p><h3>The Importance of Clarity and Communication</h3><p>Great variable names make it possible to read code almost like a narrative, reducing the need for excessive comments and helping others — and your future self — understand what’s going on.</p><p>Consider this example:</p><pre>int acctWithdrawn; // number of customers with withdrawn accounts<br>string ln; // last name of the current customer</pre><p>These names require a reader to remember the comments or consult documentation to understand what’s going on. With clearer names, this confusion disappears:</p><pre>int  customerWidthdrawnAccount;<br>string customerLastName;</pre><p>These improved names reveal the variables’ purposes directly. Code, like any text, should be intuitive and readable.</p><h3>If You Can’t Name It, You Don’t Understand It</h3><p>If you struggle to name something, it’s likely because you don’t fully understand it yet. Naming variables often requires crystal-clear thinking about what the code is trying to accomplish, so unclear names often signal unclear ideas. If you find yourself reaching for vague terms, try asking, “What exactly is this variable representing?” This exercise helps you clarify your logic, which is beneficial not only for naming but for the overall structure of your code.</p><p>To help with this, try using a “descriptive placeholder” name, even if it’s excessively long. For instance, instead of trying to name a counter vaguely, use something like numberOfActiveUsersWhereXisTrue. This will both clarify the variable&#39;s purpose for now and signal for future refactoring to find a concise, precise name as the code becomes more defined.</p><p>Here are two JavaScript code examples that demonstrate how to improve variable names for better readability and maintainability:</p><h3>Example 1: Poor Variable Names</h3><p>In this example, we have ambiguous and non-descriptive names, making it hard to follow the code’s intent.</p><pre>// Poor variable names<br>function calc(p, r) {<br>    let res = 0;<br>    for (let i = 0; i &lt; r.length; i++) {<br>        res += r[i];<br>    }<br>    return res * (p/100);<br>}</pre><pre>let price = calc(100, [5, 10, 15]);</pre><p><strong>Explanation</strong>:</p><ul><li>calc does not indicate what this function actually calculates.</li><li>p and r are not descriptive and don’t explain what values they represent.</li><li>res is unclear and doesn’t indicate that it’s the result of the sum of the array.</li></ul><h3>Improved Example: Good Variable Names</h3><p>Let’s rename the variables for clarity so that anyone reading the code can understand its purpose at a glance.</p><pre>// Improved variable names<br>function calculateTotalPrice(basePrice, discounts) {<br>    let totalDiscount = 0;<br>    for (let discount of discounts) {<br>        totalDiscount += discount;<br>    }<br>    return basePrice * (totalDiscount/100);<br>}</pre><pre>let totalPrice = calculateTotalPrice(100, [5, 10, 15]);</pre><p><strong>Explanation</strong>:</p><ul><li>calculateTotalPrice clearly describes the purpose of the function.</li><li>basePrice and discounts specify what values are being passed in.</li><li>totalDiscount makes it clear that it’s the total sum of discounts from the array.</li></ul><h3>Example 2: Using Contextual Variable Names</h3><p>Let’s look at a second example where the purpose of variables is unclear due to non-descriptive names.</p><pre>// Poor variable names<br>function prsnData(d, a) {<br>    for (let v of a) {<br>        console.log(`${d}: ${v}`);<br>    }<br>}</pre><pre>let name = &quot;Name&quot;;<br>let attributes = [&quot;smart&quot;, &quot;kind&quot;, &quot;hardworking&quot;];<br>prsnData(name, attributes);</pre><p><strong>Explanation</strong>:</p><ul><li>prsnData is unclear—does it mean person data or print data?</li><li>d and a don’t describe the values they hold, making the function difficult to follow.</li></ul><p><strong>Improved Version</strong>:</p><pre>// Improved variable names<br>function displayPersonAttributes(personName, attributes) {<br>    for (let attribute of attributes) {<br>        console.log(`${personName}: ${attribute}`);<br>    }<br>}</pre><pre>// Usage<br>let personName = &quot;Name&quot;;<br>let attributes = [&quot;smart&quot;, &quot;kind&quot;, &quot;hardworking&quot;];<br>displayPersonAttributes(personName, attributes);</pre><p><strong>Explanation</strong>:</p><ul><li>displayPersonAttributes is clear and communicates the function’s purpose.</li><li>personName and attributes indicate what each parameter represents, making the code easier to understand at a glance.</li></ul><p>Using meaningful names greatly enhances readability and shows a careful, thoughtful approach to programming.</p><h3>Characteristics of Good Variable Names</h3><p>While good names vary by context, here are some principles to help make your naming conventions solid:</p><ol><li><strong>Be Descriptive but Concise:</strong> Names should convey purpose. For instance, instead of x, userAge is much clearer. At the same time, avoid overly long names that may become cumbersome. Strive for a balance where a variable name is as short as possible while still conveying its purpose. If you have trouble find a good variable name, use aforementioned &quot;descriptive placeholder&quot;, and come back to it later.</li><li><strong>Use Context:</strong> Variable names should fit the function or class they’re in. For instance, a variable in a function handling order data might use orderTotal or customerID rather than something generic like data or num.</li><li><strong>Follow Naming Conventions:</strong> Many languages have specific naming conventions (like camelCase in JavaScript and snake_case in Python). These conventions keep code style consistent and make it easier to read across different projects.</li><li><strong>Avoid Cryptic Abbreviations:</strong> Shortening words can lead to confusion, especially if the abbreviation isn’t universally recognized. A name like num may be clear enough, but cnt could confuse readers who don&#39;t know whether it stands for &quot;count,&quot; &quot;content,&quot; or something else.</li><li><strong>Meaningful Prefixes and Suffixes:</strong> For certain data types or structures, adding a prefix can improve clarity. For example, use listOfUsers to indicate an array or list, or isEnabled to show that a variable is a boolean. These prefixes serve as quick cues about the variable&#39;s function and type.</li></ol><h3>Refactoring and Renaming When Necessary</h3><p>As code evolves, so should variable names. It’s common to start with one name and later realize it no longer reflects the variable’s purpose accurately. If your variable’s name no longer aligns with its function, take the time to rename it. Many modern IDEs such as VSCode support <a href="https://code.visualstudio.com/docs/editor/refactoring">refactor-&gt;rename</a> features, which make renaming painless across large codebases.</p><h3>Naming as a Skill to Build Over Time</h3><p>Variable naming is a skill that improves with practice and attention. Here are a few strategies to strengthen your naming skills:</p><ul><li><strong>Practice Mindfulness:</strong> When choosing a name, take a moment to consider how it reflects the variable’s role in the code.</li><li><strong>Review and Refine:</strong> As you revisit old code, critique your own variable names and improve them.</li><li><strong>Ask for Feedback:</strong> Code reviews are invaluable for learning from other perspectives. Peers can offer insights into alternative names that may be clearer.</li></ul><p>Naming variables may sound trivial, but it’s one of the most telling signs of a programmer’s skill and thoughtfulness. A programmer who takes time to name variables thoughtfully is often the one who approaches problems methodically, communicates clearly, and cares about long-term maintainability.</p><h3>About the Author</h3><p>The author is a veteran web developer who created the popular PHP datagrid tool (<a href="https://phpgrid.com/">phpgrid.com</a>), harnessing the power of CRUD to make the world a better place — at least for developers looking to simplify their lives!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b6889a8c80aa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Transform HTML Table into Card View Using Nothing But CSS]]></title>
            <link>https://medium.com/@chensformers/transform-html-table-into-card-view-using-nothing-but-css-d1e6423a5958?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/d1e6423a5958</guid>
            <category><![CDATA[cardview]]></category>
            <category><![CDATA[html-table]]></category>
            <category><![CDATA[datagrid]]></category>
            <category><![CDATA[css-tips]]></category>
            <category><![CDATA[css]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Thu, 24 Oct 2024 17:07:02 GMT</pubDate>
            <atom:updated>2024-11-18T06:29:04.533Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*InlcD9GhanS7xMbCkOy4nw.png" /><figcaption>Table to Card Transformation (credit: made with Photopea)</figcaption></figure><p>I’d like share a recent experiment that explores how to transform a plain, old-fashioned HTML table into a dynamic card view, going beyond the traditional rows and columns.</p><h3>Start With a Simple HTML Table</h3><p>Let’s begin with a simple HTML table such as the following.</p><pre>&lt;table&gt;<br>  &lt;thead&gt;<br>    &lt;tr&gt;<br>      &lt;th&gt;Company&lt;/th&gt;<br>      &lt;th&gt;Contact&lt;/th&gt;<br>      &lt;th&gt;Country&lt;/th&gt;<br>    &lt;/tr&gt;<br>  &lt;/thead&gt;<br>  &lt;tbody&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Alfreds Futterkiste&lt;/td&gt;<br>    &lt;td&gt;Maria Anders&lt;/td&gt;<br>    &lt;td&gt;Germany&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Centro Moctezuma&lt;/td&gt;<br>    &lt;td&gt;Francisco Chang&lt;/td&gt;<br>    &lt;td&gt;Mexico&lt;/td&gt;<br>  &lt;/tr&gt;  <br>  &lt;tr&gt;<br>    &lt;td&gt;Alfreds &lt;/td&gt;<br>    &lt;td&gt;Maria &lt;/td&gt;<br>    &lt;td&gt;Germany&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Centro  &lt;/td&gt;<br>    &lt;td&gt;Francisco Chang&lt;/td&gt;<br>    &lt;td&gt;Mexico&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Alfreds &lt;/td&gt;<br>    &lt;td&gt;Maria &lt;/td&gt;<br>    &lt;td&gt;Germany&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Centro comercial &lt;/td&gt;<br>    &lt;td&gt;Francisco &lt;/td&gt;<br>    &lt;td&gt;Mexico&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Alfreds &lt;/td&gt;<br>    &lt;td&gt;Maria Anders&lt;/td&gt;<br>    &lt;td&gt;Germany&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;tr&gt;<br>    &lt;td&gt;Centro comercial &lt;/td&gt;<br>    &lt;td&gt;Francisco &lt;/td&gt;<br>    &lt;td&gt;Mexico&lt;/td&gt;<br>  &lt;/tr&gt;<br>  &lt;/tbody&gt;<br>&lt;/table&gt;</pre><p>It looks like this when rendered in browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TiW1UOBRHz7rqxuOuZ2usg.png" /><figcaption>Just another html table</figcaption></figure><p>Nothing fancy.</p><p>By definition, tables consist of rows and columns. How can we transform the traditional rows and columns layout into something more dynamic?</p><h3>Discover the Power of CSS Grid</h3><p>Tables don’t have to be boring. With a few simple CSS tricks, you can easily transform a traditional HTML table into a sleek list or card view.</p><p>The best part? No JavaScript, just pure CSS!</p><p>CSS grid has been an <a href="https://www.w3.org/TR/css-grid-3/">W3C Candidate Recommendation Draft</a> since 2007, however, it has been adopted by the recent versions of <a href="https://caniuse.com/css-grid">all current major browsers</a>.</p><p>CSS grid is designed for both rows and columns, making it ideal for complex layouts such as table. It allows you to manage both horizontal and vertical alignments simultaneously, which gives you much more control than <a href="https://medium.com/r?url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FCSS%2FCSS_grid_layout%2FRelationship_of_grid_layout_with_other_layout_methods">Flexbox</a>, which is primarily one-dimensional (row or column).</p><h4>CSS Grid Properties to Use</h4><ol><li>Use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout">CSS grid layou<strong>t</strong></a><strong> </strong>for &lt;thead&gt; and &lt;tbody&gt;.</li><li>Use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/display">CSS display property</a> and set all &lt;td&gt; to be block elements</li></ol><pre>table tbody, table thead {<br>  display: grid;<br>}<br>table td {<br>  display: block;<br>}</pre><p>With the CSS above, our plain HTML table already magically transforms into a responsive list view, displaying each record neatly in a single column.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OPyaeVv6cVTv09yt6WlF-w.png" /></figure><p>It’s looking good but a bit chaotic! Let’s sprinkle on some CSS borders to give each row in our list a little breathing room.</p><pre>table, th, tr {<br>  border: 1px solid black;<br>}</pre><p>There you go. Check out the new look! Not too shabby for a list view created without a single line of JavaScript!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-YKqTLJwH--IKdNBxRfkAA.png" /></figure><p>Now we got a nice list made from an old-fashioned html table, how do we turn that nice list into a card view?</p><p>Spoiler alert: just sprinkle on a few more lines of CSS!</p><h3><strong>Transform List into Card View</strong></h3><p>Our final card trick to transform table into cards is to use CSS grid property <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns">grid-template-columns</a>:</p><pre>table tbody {<br>  display: grid;<br>  grid-template-columns: repeat(4, 1fr);<br>}</pre><p>grid-template-columns is a CSS property used in the CSS Grid layout to define the structure of the grid&#39;s columns. It specifies the number of columns, their widths, and how the space within the grid is divided.</p><p>With the repeat() function, the first parameter lets us decide how many columns we want—let’s say 4, because who doesn’t love a nice round number? The second parameter tells those columns how big to be—1fr, or one fraction of the available space. It’s like giving your columns a little pep talk: &#39;You all get an equal slice of the space pie!&#39;</p><h4>Our final card view</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*v9EDBjRgYsVa2tRuHsDKBA.png" /></figure><p>Take a moment to explore the code and see the results for yourself over on CodePen. It’s the perfect place to experiment and play around with CSS grid transformations. You may even discover some fun surprises along the way.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fchenster%2Fembed%2Fpreview%2FmdNVXPd%3Fdefault-tabs%3Dhtml%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DmdNVXPd&amp;display_name=CodePen&amp;url=https%3A%2F%2Fcodepen.io%2Fchenster%2Fpen%2FmdNVXPd&amp;image=https%3A%2F%2Fshots.codepen.io%2Fusername%2Fpen%2FmdNVXPd-512.jpg%3Fversion%3D1727834638&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/3fb51975272f1251d1535aaf7fad5775/href">https://medium.com/media/3fb51975272f1251d1535aaf7fad5775/href</a></iframe><p>Keep in mind that CSS Grid is also responsive, providing developers with enhanced control over how layouts adjust and reflow across various screen sizes and devices.</p><h3><strong>Optional: Adding data-label to card view</strong></h3><p>While the card view is visually appealing, it lacks the clarity of column information, leaving users to guess the data represented in each card.</p><p>By incorporating a touch of JavaScript, we can seamlessly add data labels for each column, enhancing the association between the labels and their corresponding cells.</p><pre>// Store each column header to array<br>const labels = [];<br>document.querySelectorAll(&#39;table thead th&#39;).forEach(th =&gt; {<br>    labels.push(th.textContent);<br>});<br><br>// Add data-label attribute to each cell<br>document.querySelectorAll(&#39;table tbody tr&#39;).forEach(tr =&gt; {<br>    tr.querySelectorAll(&#39;td&#39;).forEach((td, column) =&gt; {<br>        td.setAttribute(&#39;data-label&#39;, labels[column]);<br>    });<br>});</pre><p>It now looks like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KRrU-RdYjBpwfKULhuFk6Q.png" /></figure><p><a href="https://demo.phpcontrols.com/lib/phpGrid/examples/card_view.php"><strong>Demo</strong></a></p><p>It’s nothing like the html table that we started with. With CSS Grid, the layout options are endless because it allows for full control over both rows and columns in a two-dimensional space.</p><p><strong>Conclusion</strong></p><p>This tutorial only scratches the surface of the iceberg. You can easily create more responsive layouts, overlap elements, span items across multiple rows or columns, and adjust grid areas dynamically, making it highly versatile for various layout needs.</p><p>Happy gridding!</p><h3>About the Author</h3><p>The author is a veteran web developer who created the popular PHP datagrid tool (<a href="https://phpgrid.com/">phpgrid.com</a>), harnessing the power of CRUD to make the world a better place — at least for developers looking to simplify their lives!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d1e6423a5958" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making Grid for CRUD with pythonGrid]]></title>
            <link>https://medium.com/@chensformers/making-grid-for-crud-with-pythongrid-1d05d355f238?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/1d05d355f238</guid>
            <category><![CDATA[crud]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[flask-framework]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[datagrid]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Wed, 02 Oct 2024 00:51:58 GMT</pubDate>
            <atom:updated>2024-10-02T00:52:51.320Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*NoK79H0KqLtNgzux" /><figcaption>Photo by <a href="https://unsplash.com/@hishahadat?utm_source=medium&amp;utm_medium=referral">Shahadat Rahman</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://github.com/pycr/pythongrid">pythonGrid</a> is a new free open-source library to create a fully working datagrid for CRUD (Create, Read, Update, &amp; Delete) for <a href="https://palletsprojects.com/p/flask/">Flask</a> that connects to a relational database such as <a href="https://www.postgresql.org/">Postgres</a> or <a href="https://mariadb.org/">MySql/MariaDB</a> database.</p><p>It makes everyday datagrid tasks extremely easy. Standard functions like sorting, pagination, search, and CSV export are supported out-of-box without complicated programming.</p><p>pythonGrid does not require creating a separate data model for each database table.</p><p><strong>It requires only two lines of code for a basic CRUD.</strong></p><pre>mygrid = PythonGrid(&#39;SELECT * FROM TABLE_NAME&#39;, &#39;PRIMARY_KEY&#39;, &#39;TABLE_NAME&#39;)<br>return render_template(&#39;template.html&#39;, title=&#39;a page title&#39;, grid=mygrid)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dDTEZctUIlXbOjw-1uwSpg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/72/0*ejAdsoRHIbDpkKOF.png" /><figcaption>For real? That’s it??</figcaption></figure><p>Yes! That’s it! It is because <strong>pythonGrid does not require </strong><a href="https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/"><strong>declaring data models</strong></a><strong>, nor need to manage table relationships.</strong> A database table name is all you need.</p><h3>Quick Start</h3><p>A couple of quick-start options are available:</p><ul><li><a href="https://github.com/pycr/pythongrid/archive/master.zip">Download the latest release</a></li><li>Clone the repo (recommended):</li></ul><pre>git clone <a href="https://github.com/pycr/pythongrid.git">https://github.com/pycr/pythongrid.git</a></pre><h3>Requirements</h3><ul><li><a href="https://github.com/pycr/pythongrid">pythonGrid</a></li><li>Python 3.6</li><li>Flask</li><li>SQLAlchemy</li><li>MySQL or Postgres</li></ul><h3>Files included</h3><p>Within the download you will see something like this:</p><pre>├── LICENSE<br>├── README.md<br>├── app<br>│   ├── __init__.py<br>│   ├── data.py<br>│   ├── grid.py<br>│   ├── export.py<br>│   ├── routes.py<br>│   ├── static<br>│   └── templates<br>│       ├── 404.html<br>│       ├── base.html<br>│       ├── grid.html<br>│       └── index.html<br>├── sample<br>│   ├── sampledb_postgres.sql<br>│   ├── sampledb_mysql.sql<br>├── config.py<br>├── index.py<br>└── requirements.txt</pre><p>pythonGrid has three main files in grid.py, data.py, and export.py in app folder.</p><ul><li>grid.py is the main Python class that is responsible for creating the datagrid table. It&#39;s a high-level wrapper to <a href="https://free-jqgrid.github.io/getting-started/index.html">jqGrid</a>, a popular jQuery datagrid plugin, for rendering datagrid in the browser.</li><li>data.py is a Python class that returns the data via AJAX to populate the grid from a database.</li><li>export.py is responsible for handling the data export.</li><li>static contains all of the client-side Javascript and CSS files used for rendering.</li></ul><h3>Creating the Database</h3><p>Find the sample database in the folder <a href="https://github.com/pycr/pythongrid/blob/master/app/sample/">sampledb</a>. Using your favorite MySQL os Postgres client (more database supports are coming).</p><ol><li>Create a new database named sampledb</li><li>Run the sample SQL script.</li></ol><h3>Install Python</h3><p>First of all, if you don’t have Python installed on your computer, download and install it from the <a href="https://www.python.org/downloads/">Python official website</a> now.<br>To make sure your Python is functional, type python3 in a terminal window, or just python if that does not work. Here is what you should expect to see:</p><pre>Python 3.6.3 (v3.6.3:2c5fed86e0, Oct  3 2017, 00:32:08)<br>[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin<br>Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; <strong>for</strong> more information.<br>&gt;&gt;&gt;</pre><p>Next, install the Flask framework.</p><h3>Install Flask Framework via Virtual Environment</h3><p>It is highly recommended to use <a href="https://docs.python.org/3/tutorial/venv.html">Python virtual environment</a>. A Python virtual environment is a self-contained separate copy of Python installation. Different applications can then use different virtual environments with a modified Python copy without worrying about system permissions.</p><p>The following command will create a virtual environment named venv stored in a directory, also called venv.</p><pre>python3 -m venv venv</pre><p>Activate the new virtual environment:</p><pre>source venv/bin/activate</pre><p>Now the terminal prompt is modified to include the name of the activated virtual environment</p><pre>(venv) $ _</pre><p>With a new virtual environment created and activated, finally, let’s install dependents:</p><h3>Install Dependents</h3><p>pythonGrid uses <a href="https://www.sqlalchemy.org/">SQLAlchemy</a> to support different types of databases.</p><pre>pip install -r requirements.txt</pre><h3>Configuration</h3><p>Find file config.py, and set the database connection properties according to your environment. The demo uses the MySQL database.</p><p>You can also use a socket to connect to your database without specifying a database hostname.</p><pre>PYTHONGRID_DB_HOSTNAME = &#39;mysqldatabase.example.com&#39;<br>PYTHONGRID_DB_NAME = &#39;sampledb&#39;<br>PYTHONGRID_DB_USERNAME = &#39;root&#39;<br>PYTHONGRID_DB_PASSWORD = &#39;root&#39;<br>PYTHONGRID_DB_TYPE = &#39;mysql+pymysql&#39;</pre><p>For Postgres set database type to postgres+psycopg2</p><pre>PYTHONGRID_DB_TYPE = &#39;postgres+psycopg2&#39;</pre><h3>Initialize Grid</h3><p>Flask uses <em>view functions</em> to handle application routes. View functions are mapped to one or more route URLs so that Flask knows what logic to execute when a client requests a given URL such as “https://example.com/grid&quot;.</p><p>We have three view functions that need initialization.</p><h3>index()</h3><p>The file routes.py contains our def index() view functions associate with root URL /. This means that when a web browser requests the URL, Flask invokes this function and passes the return value of it back to the browser as a response.</p><p>Inside the function, it creates a new instance of the PythonGrid class and assigns this object to the local variable grid. Note orders is a table from our sample database <a href="https://github.com/pycr/pythongrid/blob/master/app/sample/">sampledb</a>.</p><pre>grid = PythonGrid(&#39;SELECT * FROM orders&#39;, &#39;orderNumber&#39;, &#39;orders&#39;)</pre><p>PythonGrid initializer shown above requires 3 parameters:</p><ol><li>A simple SQL SELECT statement</li><li>The database table primary key</li><li>The database table name</li></ol><p>The view function pass the grid object into the rendered template from grid.html template.</p><pre><strong>return</strong> render_template(&#39;grid.html&#39;, title=&#39;GRID&#39;, grid=grid)</pre><h3>data()</h3><p>Next, we need the data for the grid (thus the datagrid</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/72/0*CRVw8TEIRwqrvUOp.png" /></figure><p>In the next view function data(), we create a new instance for PythonGridDbData class that is responsible for retrieve data from the database to populate our datagrid.</p><p>PythonGridDbData class requires only 1 parameter, which should be the same SQL SELECT statement used for PythonGrid class.</p><pre>data = PythonGridDbData(&#39;SELECT * FROM orders&#39;)<br><strong>return</strong> data.getData()</pre><h3>export()</h3><p>The export function is almost identical to the data function above except we need to use PythonGridDbExport to initiate a new instance for the export class.</p><pre>exp = PythonGridDbExport(&#39;SELECT * FROM orders&#39;)<br><strong>return</strong> exp.export()</pre><h3>Hello, Grid</h3><p>At this point, we can run our program with the command below.</p><pre>flask <strong>run</strong></pre><p>It should give you a beautiful datagrid with data from orders table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*NR9x85VbB2Okczp8" /></figure><h3>A List of Common Datagrid Functions</h3><p>From the basic grid, we can add new functions such as changing title, adding search, and enabling export, set text-align, etc., through simple function calls.</p><p><a href="https://pythongrid.com/set_caption/"><strong>Datagrid Caption</strong></a></p><pre><strong>grid</strong>.set_caption(&#39;Orders Table&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/818/0*cuD_ZxuKJpcV8aMi" /></figure><p><a href="https://pythongrid.com/set_caption/"><strong>Column Title</strong></a></p><pre><strong>grid</strong>.set_col_title(&#39;orderNumber&#39;, &#39;Order #&#39;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1016/0*YAxVdgAdNTLX8TsS" /></figure><p><a href="https://pythongrid.com/set_col_hidden/"><strong>Hide Columns</strong></a></p><pre>grid.set_col_hidden([&#39;customerNumber, logTime, shippedDate, requiredDate&#39;])</pre><p><a href="https://pythongrid.com/set_pagesize/"><strong>Set Page Size</strong></a><strong> (# of rows to display per page)</strong></p><pre>grid.set_pagesize(20)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/822/0*ccgUCcIhO_XOedKB" /></figure><p><a href="https://pythongrid.com/set_dimension/"><strong>Set Datagrid Dimension</strong></a><strong> (e.g. Width 800px, Height 400px)</strong></p><pre>grid.set_dimension(800, 400)</pre><p><a href="https://pythongrid.com/enable_search/"><strong>Enable Search</strong></a></p><pre>grid.enable_search(True)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IRO5AEyCtyKukxWD" /></figure><p><a href="https://pythongrid.com/enable_rownumbers/"><strong>Display Row Number</strong></a></p><pre>grid.enable_rownumbers(True)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*f0q7RaqgfiSyjSA0" /></figure><p><a href="https://pythongrid.com/enable_pagecount/"><strong>Display Page Count</strong></a><strong> on Toolbar</strong></p><pre>grid.enable_pagecount(True)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/0*iALIWY0Zkkdw7Qdj" /></figure><p><a href="https://pythongrid.com/set_col_align/"><strong>Column Text Align</strong></a><strong> (e.g. Left, Center, or Right)</strong></p><pre><strong>grid</strong>.set_col_align(&#39;status&#39;, &#39;center&#39;)</pre><p><a href="https://pythongrid.com/set_col_width/"><strong>Set Column Width</strong></a><strong> (e.g. 600px)</strong></p><pre><strong>grid</strong>.set_col_width(&#39;comments&#39;, 600)</pre><p><strong>Enable </strong><a href="https://pythongrid.com/enable_export/"><strong>CSV export</strong></a></p><pre><strong>grid</strong>.enable_ex<strong>port</strong>()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*q104L-Cbi8dSPLtr" /></figure><p>See the list of complete <a href="https://pythongrid.com/documentation/">pythonGrid documentation</a>.</p><ul><li><a href="https://demo.pythongrid.com/">Demo</a></li><li><a href="https://pythongrid.com/">Project Homepage</a></li></ul><p>Please stay tuned for the second part of the step-by-step walkthrough for the rest of CRUD operations, including Add, Edit, and Delete!</p><p>If you have any questions about this tutorial, feel free to comment below</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d05d355f238" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[All You Need Is CRUD]]></title>
            <link>https://medium.com/@chensformers/all-you-need-is-crud-374dda40e7b4?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/374dda40e7b4</guid>
            <category><![CDATA[datagrid]]></category>
            <category><![CDATA[https]]></category>
            <category><![CDATA[internet]]></category>
            <category><![CDATA[www]]></category>
            <category><![CDATA[crud]]></category>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Thu, 12 Sep 2024 03:33:33 GMT</pubDate>
            <atom:updated>2024-09-12T03:33:33.043Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QCqgRNj-NMDyNXxEBtXySA.jpeg" /><figcaption>Image credit: Bing Image Creator</figcaption></figure><p>I’ve been itching to write about this topic for some time now.</p><p>Believe it or not, everything is essentially CRUD on the internet. It is the hidden skeleton that supports all of our digital interactions.</p><h3>What is CRUD?</h3><p>First of all, let’s get the jargon out of the way. CRUD stands for <strong>Create, Read, Update, and Delete</strong> — the four basic operations performed on data in a database or application.</p><p>It’s not just about data entry forms or database management; CRUD operations are embedded in nearly every feature and action we perform in applications, including those that might seem more complex at first glance.</p><p>Let’s start with an example.</p><h3>SAP is a massive CRUD</h3><p>During my years in grad school, I had the pleasure (or displeasure) to use SAP ERP system The first time I launched SAP (back when it was still a Windows app), I was struck by its overwhelming number of datagrids. And not just a few — TONS of them, all intricately linked.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/970/0*-HZH1lWvFdx6oEid.png" /><figcaption>SAP interface with datagrids</figcaption></figure><p>Imagine navigating through this cluttered interface — it’s a German product, after all!</p><p>Despite its chaotic appearance, SAP’s core is packed with datagrids. These datagrids with columns and rows are fundamentally tied to CRUD operations (Create, Read, Update, Delete). They ensure that managing vast amounts of data is both efficient and intuitive, thanks to CRUD’s reliable framework.</p><p>SAP has improved is UI over the years, but datagrid still play an essential role at its core.</p><p>When there is datagrid, there must be CRUD (Create, Read, Update, Delete).</p><h3>HTTP verbs are CRUD</h3><p>Now, let’s talk about something you might use every day without realizing its connection to CRUD — HTTP verbs.</p><p>HTTP verbs (or methods) GET, POST, PUT, DELETE, and PATCH are essentially aligned with CRUD operations because they map directly to the fundamental actions required to manage resources in a web application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EsIQsFmwTODkOpm9o0Jxlg.png" /><figcaption>Image credit: useful-web</figcaption></figure><p>Here’s how each HTTP verb corresponds to CRUD:</p><p><strong>1. GET → Read</strong></p><ul><li><strong>GET</strong> is used to retrieve data from a server, which corresponds to the <strong>Read</strong> operation in CRUD. For example, when you visit a webpage or query an API to fetch information, the server processes a GET request to retrieve and return the requested data.</li></ul><p><strong>2. POST → Create</strong></p><ul><li><strong>POST</strong> is used to send data to the server to create a new resource. This aligns with the <strong>Create</strong> operation in CRUD. For instance, submitting a form to create a new user account or adding a new item to a database typically involves a POST request.</li></ul><p><strong>3. PUT → Update</strong></p><ul><li><strong>PUT</strong> is used to update an existing resource on the server. This corresponds to the <strong>Update</strong> operation in CRUD. PUT requests typically involve sending the complete updated resource to the server, which then replaces the existing resource with the new version</li></ul><p><strong>4. DELETE → Delete</strong></p><ul><li><strong>DELETE</strong> is used to remove a resource from the server, which directly aligns with the <strong>Delete</strong> operation in CRUD. When a DELETE request is sent, the specified resource is removed from the server, reflecting the removal of a record in a database.</li></ul><p>By designing HTTP verbs around CRUD concepts, web APIs can manage resources in a way that is both intuitive for developers and effective for data manipulation, maintaining consistency across different systems.</p><h3>Not Convinced? Let’s Dig Deeper</h3><p>You might be thinking, “Okay, there are lots of CRUDs, but does that really mean everything is CRUD?” You might argue that complex applications like Facebook or YouTube don’t fit into this model. Or do they?</p><h3>Why Facebook is also just a CRUD</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9Ql9kog6LHvZmc0DtBmJOw.png" /></figure><p>In 2016, Anatoly Lubarsky reverse-engineered Facebook’s database schema, revealing that at its core with user data is stored in relational databases, and CRUD operations manage and retrieve this data.</p><p>Whether it’s creating posts, reading news feeds, updating profiles, or deleting content, CRUD operations ensure data is consistently handled in the background, even as the complex processes remain invisible to the user.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kjOxtpl4fk7_8TJO" /><figcaption>Facebook’s Database reverse engineered by Anatoly Lubarsky</figcaption></figure><p>Users interact with the platform by creating posts, reading news feeds, updating profiles, and deleting content, all of which are underpinned by CRUD operations that ensure the data is consistently stored, retrieved, and modified in the database, while the complex backend processes remain invisible to the user.</p><p>Let’s break it down:</p><h4>1. FB User Login</h4><ul><li><strong>Read</strong>: When you log in to FB, the system reads (R) your input credentials (username and password) from the database to verify your identity.</li><li><strong>Update</strong>: The system might update (U) the last login timestamp or session information in the database once you’re authenticated.</li><li><strong>Create</strong>: If you log in for the first time or create a session, the system might create © a new session record.</li><li><strong>Delete</strong>: Old session data or failed login attempts might be deleted (D) as part of maintaining security.</li></ul><h4>2. Reading FB posts</h4><ul><li><strong>Read</strong>: Search operations are primarily about reading (R) data. When you search for something, the system queries the database and reads the relevant records that match your search criteria.</li><li><strong>Create/Update/Delete</strong>: Though less obvious, search engines also involve creating, updating, and deleting indexes in the background to ensure fast and accurate results. For instance, when new content is added or updated on a platform, search indexes are updated (U) or new entries are created ©. When content is removed, those indexes are deleted (D).</li></ul><h4>3. Updating Profile</h4><ul><li>Updating profiles is a CRUD operation because it involves modifying existing data in the database. When a user changes their profile information, the system updates the corresponding record in the database</li></ul><p>Still not convinced?</p><p>Let’s take a look at YouTube.</p><h3>YouTube: A CRUD Classic</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SpMzoAmp4EPOtXeBd4kHoA.png" /></figure><p>YouTube is actually a great example of a CRUD application. Here’s how YouTube fits into this model:</p><ol><li><strong>Create<br>Uploading Videos:</strong> Users can create content by uploading videos. This involves adding a new video record to the YouTube database, including metadata like the title, description, tags, and thumbnail.</li><li><strong>Read<br>Watching Videos:</strong> The core functionality of YouTube is allowing users to read (consume) content. This involves retrieving video data from the database and streaming it to the user.<br>Searching and Browsing: Users can search for videos, browse recommendations, and view playlists, all of which involve reading data from YouTube’s servers.</li><li><strong>Update<br>Editing Videos: </strong>Users can update the metadata of their videos, such as changing the title, description, or privacy settings. <br>Managing Playlists: Users can update their playlists by adding or removing videos, changing the order, or editing the playlist title and description.</li><li><strong>Delete</strong><br><strong>Deleting Videos:</strong> Users can delete their videos, which removes the video and its associated data from YouTube’s database.<br>Removing Content from Playlists: Users can also delete videos from playlists or remove entire playlists.</li></ol><h3>The Myth of Non-CRUD Operations</h3><p>What I heard the most is that people would say, “My application has non-CRUD operations,” usually referring to complex functionalities that go beyond basic data management, such as real-time data processing, advanced analytics, complex business logic, or workflow automation, which involve more than just creating, reading, updating, or deleting records in a database.</p><p>Or are they?</p><h3>CRUD in disguise</h3><p>Datagrid is a CRUD, but not all CRUDs are datagrids. CRUD could have many forms and faces.</p><p>Even in situations involving complex functionalities like real-time data processing, advanced analytics, or workflow automation, CRUD operations are still essentially everywhere. These advanced features still rely on the underlying ability to create, read, update, or delete data as they interact with databases or data stores.</p><h4><strong>1. Real-Time Data Processing:</strong></h4><ul><li>In a real-time data system such as stock trading platform that processes real-time data, CRUD operations are still involved. For instance, the system <em>creates</em> new trade records when transactions occur, <em>reads</em> real-time market data to display to users, <em>updates</em> users’ portfolios with the latest values, and <em>deletes</em> outdated or canceled orders. While the processing might be complex, the data handling at its core is still CRUD-based.</li></ul><h4>2. Advanced Analytics:</h4><ul><li>In a business intelligence tool that performs advanced analytics, CRUD operations are essential. The tool <em>creates</em> datasets based on user queries, <em>reads</em> large volumes of data from various sources, <em>updates</em> dashboards with the latest analysis results, and <em>deletes</em> outdated reports. The sophisticated algorithms and visualizations rely on CRUD to manage and display the underlying data effectively.</li></ul><h4>3. Workflow Automation:</h4><ul><li>When you automate a workflow, what’s really happening? The system <strong>creates</strong> new tasks, <strong>reads</strong> existing data (because it’s nosy like that), <strong>updates</strong> the status as things move along, and <strong>deletes</strong> anything that’s done or obsolete, kind of like cleaning out your closet but with data instead of last season’s shoes. So no matter how complex or high-tech the automation gets, at its core, it’s just CRUD — on autopilot, and maybe with a little more flair.</li></ul><h4>4. Machine Learning Applications:</h4><ul><li>In a machine learning platform, CRUD operations are involved in managing training datasets and model results. Even though machine learning is a mystical dance of algorithms and data, it’s still fueled by the trusty old CRUD operations — because even fancy AI models need their data served fresh, updated, and occasionally deleted.</li></ul><p>Even though the applications may perform sophisticated tasks, add, read, update, delete operations are still the backbone of any data management system. CRUD remains essential to ensure that the application’s complex functionalities can operate effectively.</p><p>The list goes on and on.</p><h3>Once You Spot CRUD, You’ll Start Seeing It Everywhere!</h3><p>Once you understand how CRUD works, you’ll start recognizing its patterns in all kinds of software.</p><p>These four magic letters are so fundamental to data management, it quietly powers a wide variety of interactions — even ones that don’t seem obvious at first glance. The more familiar you are with this concept, the easier it becomes to see how deeply integrated it is in almost every digital system.</p><p>It’s like discovering the hidden building blocks behind the digital world.</p><p>All you need is CRUD!</p><h3>About the Author</h3><p>The author is a veteran web developer who created the popular PHP datagrid tool (<a href="https://phpgrid.com">phpgrid.com</a>), harnessing the power of CRUD to make the world a better place — at least for developers looking to simplify their lives!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=374dda40e7b4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Inventory Management with Dashboard, User Management in Laravel 10 & Bootstrap 5]]></title>
            <link>https://medium.com/hackernoon/inventory-management-with-dashboard-user-management-in-laravel-10-bootstrap-5-60ff052dc721?source=rss-d80825e3e646------2</link>
            <guid isPermaLink="false">https://medium.com/p/60ff052dc721</guid>
            <dc:creator><![CDATA[Richard]]></dc:creator>
            <pubDate>Sat, 12 Aug 2023 06:16:43 GMT</pubDate>
            <atom:updated>2023-08-12T06:16:43.185Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8NjwUXoGnPF7lFAehQX8Aw.png" /><figcaption>Inventory Management with Dashboard, User Management in Laravel 10, Bootstrap 5</figcaption></figure><p>This is an expansion of the popular <a href="https://medium.com/@chensformers/inventory-management-system-with-barcode-scanner-in-php-a-definitive-guide-d18fdc165511">inventory management system tutorial</a>, with <a href="https://laravel.com/docs/10.x/releases"><strong>Laravel 10</strong></a><strong>,</strong> <a href="https://getbootstrap.com/"><strong>Bootstrap 5</strong></a><strong> </strong>integrated, as an out-of-box, and also customizable solution, can be used right away with little configuration.</p><p>The inventory management system includes features like inventory tracking, stock alerts, barcode scanning, purchase order management, sales order management, and report generation. With this system, you can easily manage your inventory, keep track of your stock levels, and ensure that you have the right amount of inventory on hand at all times.</p><p>In terms of the technology used, Laravel 10 provides a robust and secure back-end framework for handling the database and business logic, while Bootstrap allows for the creation of user-friendly front-end interfaces that are optimized for mobile devices.</p><h3>System Requirements</h3><p>Our Inventory System requires the standard commercial phpGrid and phpChart license as it needs a few advanced features from both components.</p><ul><li>PHP 8.1</li><li>MySQL or MariaDB</li><li>phpGrid 7+ (free or commercial)</li><li>phpChart (for dashboard)</li></ul><h3>Inventory Management Components</h3><ul><li>Incoming shipments</li><li>Outgoing orders</li><li>Inventory</li><li>Suppliers</li><li>Dashboard</li><li>User Management</li><li>User Profile</li><li>User Register</li><li>Sign In</li><li>Sign Out</li></ul><h3>Database Diagram (updated 2023)</h3><p>The new inventory system has added a category in addition to products, purchases, orders, and suppliers. Current inventory, or products on hand, is updated by tracking incoming shipments and outgoing orders. Order alerts can be set to trigger when inventory levels fall below custom-defined minimum levels.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e9T5WFiwjONEDShEnsDCJg.png" /></figure><h3>Set up Database</h3><p>The inventory management now has two separate databases, a inventory management database, and a Laravel framework database for user and session management.</p><h3>Import Inventory Management Database</h3><p>First of all, you’ll need to create a database for your inventory management system using <strong><em>InventoryManager.sql</em></strong> SQL script in the end of this tutorial. Execute the script using a MySQL tool such as <a href="https://www.mysql.com/products/workbench/">MySQL Workbench</a>. This will create a new database named <strong><em>InventoryManager.</em></strong></p><h3>Set up Laravel System Database</h3><p>You will use Laravel’s built-in database migrations to create the necessary system tables and columns.</p><p>First of all, copy the following file</p><pre>.env.example</pre><p>to</p><pre>.env</pre><p>then updated the .env configurations (mainly the database configuration)</p><p>2. In your terminal run the following to create the database tables and seed the roles and users tables</p><pre>php artisan key:generate<br>php artisan migrate --seed</pre><h3>Set up phpGrid</h3><p>We will use a datagrid component by <a href="https://phpgrid.com/example/creating-custom-inventory-management-application-php-mysql/https//phpgrid.com">phpGrid</a> to handle all internal database <strong>CRUD (Create, Remove, Update, and Delete)</strong> operations.</p><p><a href="https://phpgrid.com/download/">Download a copy of phpGrid</a> (free or commercial)before you proceed.</p><p>To install phpGrid, follow these steps:</p><ol><li>Unzip the phpGrid download file.</li><li>Upload the <strong><em>phpGrid</em></strong> folder to the phpGrid folder.</li><li>Complete the installation by configuring the <strong><em>conf.php</em></strong> file.</li></ol><p>To set up conf.php, you can use those <a href="https://phpgrid.com/documentation/installation/">manual installation steps</a>.</p><h3>User Administration</h3><p>User administration is included as part of the improved inventory management system. The system includes features for creating and deleting user accounts, assigning and revoking user permissions, and managing user roles and groups.</p><h3>User Login</h3><p>If you are not logged in you can only access this page or the Sign Up page. The default url takes you to the login page where you use the default credentials <strong>admin@admin.com</strong> with the password <strong>secret</strong>. Logging in is possible only with already existing credentials. For this to work you should have run the migrations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3KeQm1T_ailoRtfm.png" /></figure><h3>New Users Sign-up</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bCExumHaKVzALt57.png" /></figure><p>You can register as a user by filling in the name, email, role and password for your account. For your role you can choose between the Admin, Creator and Member. It is important to know that an admin user has access to all the pages and actions, can delete, add and edit another users, other roles, items, tags or categories; a creator user has accces to category, tag and item management, but can not add, edit or delete other users; a member user has access to the item management but can not take any action.</p><p>You can do this by accessing the sign up page from the “<strong>Sign Up</strong>” button in the top navbar or by clicking the “<strong>Sign Up</strong>” button from the bottom of the log in form. Another simple way is adding <strong>/register</strong> in the url.</p><h3>Forgot Password</h3><p>If a user forgets the account’s password it is possible to reset the password. For this the user should click on the “<strong>here</strong>” under the login form or add <strong>/login/forgot-password</strong> in the url.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4RzrhBR0elu2-GMI.png" /></figure><h3>User Profile</h3><p>The profile can be accessed by a logged in user by clicking “<strong>User Profile</strong>” from the sidebar or adding <strong>/user-profile</strong> in the url. The user can add information like birthday, gender, phone number, location, language or skills.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Du4DSLr8gj5AmLQT.png" /></figure><h3>User Management</h3><p>This module allows administrators to manage user accounts and access permissions for a particular application or system. In addition to managing user accounts, user management can also be used to manage customer accounts, partners, and other types of users who need access to a particular application or system.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KKsnFSpez9rX2mJf.png" /></figure><h3>Dashboard</h3><p>What is an inventory system good for without some of type of report? In this section, you will learn how to use <a href="http://phpchart.com/">phpChart</a> — which seamlessly integrates with phpGrid — to create visually pleasing and useful reports for your Inventory Manager application.</p><p>Here’s what our dashboard:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*kEXVz6NR838rapdU.png" /></figure><h3>Quick Stats</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*37d7N6bVTFddBjKN.png" /></figure><h3>Admin</h3><p>Instead of manually update supplier and category information in database directly in the past, the improved admin page added both Supplier Management and Category Management. More management admin can be added and customized in this section.</p><h4>Supplier Management</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*z-K09udZbVpdxH-O.png" /></figure><h4>Category Management</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Iy3_97CieMclPoj3.png" /></figure><h3>Inventory Administration</h3><h4>Products</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*XrYl13bz3RfsQL4p.png" /></figure><h4>Purchases</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sBXc3cM8lVsCQfIY.png" /></figure><h4>Current Orders</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2Iswd3LNMh-iY4zo.png" /></figure><h4>Barcodes</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Qerplx6i9CE0jrr7.png" /></figure><h3>Summary</h3><p>In conclusion, with Laravel 10, Bootstrap, and pair with phpGrid can easily build a powerful solution for businesses that want to streamline their inventory management processes, improve accuracy, and reduce costs associated with inventory management. It also can be extended and tailored to meet the specific needs of any business.</p><p>Hope you like this tutorial! Don’t forget to launch the live demo!</p><p><a href="https://ims.mydatagrid.com/"><strong>LAUNCH LIVE DEMO</strong></a></p><pre>Login name:<br>admin@admin.com<br><br>Password:<br>secret</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60ff052dc721" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/inventory-management-with-dashboard-user-management-in-laravel-10-bootstrap-5-60ff052dc721">Inventory Management with Dashboard, User Management in Laravel 10 &amp; Bootstrap 5</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>