<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="https://publishpress.com/"
	>

<channel>
	<title>PHP Archives - Tomoshare</title>
	<atom:link href="https://blog.tomosia.com.vn/danh-muc/php/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.tomosia.com.vn/danh-muc/php/</link>
	<description>Kênh chia sẻ kiến thức Tomosia Việt Nam</description>
	<lastBuildDate>Fri, 17 Apr 2026 07:08:02 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.tomosia.com.vn/wp-content/uploads/2023/09/cropped-icon-32x32.png</url>
	<title>PHP Archives - Tomoshare</title>
	<link>https://blog.tomosia.com.vn/danh-muc/php/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Tối ưu Slow Query PostgreSQL: Khi một dòng code cứu cả hệ thống</title>
		<link>https://blog.tomosia.com.vn/toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong/</link>
					<comments>https://blog.tomosia.com.vn/toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong/#respond</comments>
		
		<dc:creator><![CDATA[hoa nguyen]]></dc:creator>
		<pubDate>Fri, 17 Apr 2026 07:07:57 +0000</pubDate>
				<category><![CDATA[Laravel]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=4257</guid>

					<description><![CDATA[<p>Tối ưu Slow Query PostgreSQL: Khi một dòng code cứu cả hệ thống Bối cảnh Trong một dự&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong/">Tối ưu Slow Query PostgreSQL: Khi một dòng code cứu cả hệ thống</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h1 id="toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong" class="wp-block-heading">Tối ưu Slow Query PostgreSQL: Khi một dòng code cứu cả hệ thống</h1>



<h2 id="boi-canh" class="wp-block-heading">Bối cảnh</h2>



<p>Trong một dự án e-commerce gần đây, tôi gặp một bài toán rất quen thuộc:<br>trên trang chi tiết đơn hàng, cần có nút:</p>



<ul class="wp-block-list">
<li>“Đơn tiếp theo” (Next)</li>



<li>“Đơn trước đó” (Previous)</li>
</ul>



<p>Yêu cầu tưởng đơn giản, nhưng có một constraint quan trọng:<br><strong>không được load toàn bộ danh sách lên RAM</strong>.</p>



<p>Giải pháp hiển nhiên là dùng <strong>Keyset Pagination</strong> (cursor-based), thay vì offset pagination.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="cach-trien-khai-ban-dau" class="wp-block-heading">Cách triển khai ban đầu</h2>



<p>Dữ liệu được sort theo:</p>



<ul class="wp-block-list">
<li><code>paid_at</code> (thời điểm thanh toán)</li>



<li><code>id</code> (để break tie)</li>
</ul>



<p>Code Laravel Eloquent ban đầu của tôi cho “Next Order” trông như sau:</p>



<pre class="wp-block-preformatted">-&gt;where(function (Builder $q) use ($order) {<br>    $q-&gt;where('paid_at', '&gt;', $order-&gt;paid_at)<br>        -&gt;orWhere(function (Builder $q1) use ($order) {<br>            $q1-&gt;where('paid_at', $order-&gt;paid_at)<br>                -&gt;where('id', '&gt;', $order-&gt;id);<br>        });<br>})<br>-&gt;orderBy('paid_at')<br>-&gt;orderBy('id')</pre>



<p>Nếu đọc bằng “tiếng người”, logic này hoàn toàn đúng:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Lấy các đơn có <code>paid_at</code> lớn hơn,<br>hoặc nếu cùng thời điểm thì <code>id</code> phải lớn hơn.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="van-de-thuc-te-xay-ra" class="wp-block-heading">Vấn đề thực tế xảy ra</h2>



<p>Khi dữ liệu tăng lên vài triệu bản ghi, hệ thống bắt đầu:</p>



<ul class="wp-block-list">
<li>xuất hiện <strong>slow query</strong></li>



<li>query plan chuyển sang:
<ul class="wp-block-list">
<li><code>Bitmap Heap Scan</code></li>



<li>hoặc tệ hơn là <code>Seq Scan</code></li>
</ul>
</li>
</ul>



<p>Trong khi đó, database đã có <strong>composite index</strong>:</p>



<pre class="wp-block-preformatted">(paid_at, id)</pre>



<p>=&gt; Về lý thuyết phải chạy rất nhanh.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="nguyen-nhan-cot-loi" class="wp-block-heading">Nguyên nhân cốt lõi</h2>



<p>Thủ phạm nằm ở <strong>mệnh đề <code>OR</code></strong>.</p>



<p>PostgreSQL Query Planner khi gặp <code>OR</code> thường:</p>



<ul class="wp-block-list">
<li>tách điều kiện thành nhiều nhánh</li>



<li>không tận dụng được composite index một cách hiệu quả</li>



<li>dẫn tới scan nhiều hơn cần thiết</li>
</ul>



<p>Nói đơn giản:<br><strong>Index có, nhưng không được dùng đúng cách.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="cach-giai-quyet-va-la-diem-aha-moment" class="wp-block-heading">Cách giải quyết (và là điểm “aha moment”)</h2>



<p>Thay vì viết điều kiện dạng boolean phức tạp,<br>ta sử dụng <strong>Tuple Comparison (Row Value Syntax)</strong> của PostgreSQL.</p>



<h3 id="thay-doi-duy-nhat" class="wp-block-heading">Thay đổi duy nhất:</h3>



<pre class="wp-block-preformatted">-&gt;whereRaw('(paid_at, id) &gt; (?, ?)', [$order-&gt;paid_at, $order-&gt;id])</pre>



<p>Với “Previous”:</p>



<pre class="wp-block-preformatted">-&gt;whereRaw('(paid_at, id) &lt; (?, ?)', [$order-&gt;paid_at, $order-&gt;id])</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="vi-sao-cach-nay-hieu-qua" class="wp-block-heading">Vì sao cách này hiệu quả?</h2>



<p>Composite index <code>(paid_at, id)</code> trong PostgreSQL được sắp xếp theo kiểu:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>từ điển (lexicographical order)</p>
</blockquote>



<p>Tức là:</p>



<ol class="wp-block-list">
<li>So sánh <code>paid_at</code></li>



<li>Nếu bằng nhau → so sánh <code>id</code></li>
</ol>



<p>Khi bạn viết:</p>



<pre class="wp-block-preformatted">(paid_at, id) &gt; (x, y)</pre>



<p>PostgreSQL có thể:</p>



<ul class="wp-block-list">
<li><strong>map trực tiếp vào B-Tree index</strong></li>



<li>nhảy đúng vị trí <code>(x, y)</code></li>



<li>thực hiện <strong>Index Scan tuyến tính</strong></li>
</ul>



<p>=&gt; Không cần phân nhánh logic như <code>OR</code> nữa.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="ket-qua" class="wp-block-heading">Kết quả</h2>



<p>Sau khi thay đổi:</p>



<ul class="wp-block-list">
<li>Query Plan chuyển thành <strong>Index Scan</strong></li>



<li>Slow query biến mất</li>



<li>Thời gian response ổn định lại</li>
</ul>



<p>Ngoài ra còn một lợi ích “không ngờ”:</p>



<h3 id="code-gon-hon-rat-nhieu" class="wp-block-heading">Code gọn hơn rất nhiều</h3>



<p>Từ:</p>



<ul class="wp-block-list">
<li>nhiều closure lồng nhau</li>



<li>logic khó đọc</li>
</ul>



<p>=&gt; còn:</p>



<pre class="wp-block-preformatted">-&gt;whereRaw('(paid_at, id) &gt; (?, ?)', [...])</pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="bai-hoc-rut-ra" class="wp-block-heading">Bài học rút ra</h2>



<h3 id="1-tranh-or-khi-lam-keyset-pagination" class="wp-block-heading">1. Tránh <code>OR</code> khi làm keyset pagination</h3>



<p>Đặc biệt khi:</p>



<ul class="wp-block-list">
<li>sort theo nhiều cột</li>



<li>đã có composite index</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 id="2-hieu-cach-database-to-chuc-index-quan-trong-hon-orm" class="wp-block-heading">2. Hiểu cách database tổ chức index quan trọng hơn ORM</h3>



<p>ORM giúp code “đẹp”, nhưng:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>không đảm bảo query tối ưu</p>
</blockquote>



<p>Đôi khi, quay về <strong>RAW SQL đúng chỗ</strong> lại là lựa chọn tốt hơn.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h3 id="3-tan-dung-tuple-comparison-trong-postgresql" class="wp-block-heading">3. Tận dụng Tuple Comparison trong PostgreSQL</h3>



<p>Đây là một feature rất mạnh nhưng ít được dùng:</p>



<pre class="wp-block-preformatted">(col1, col2) &gt; (val1, val2)</pre>



<p>Rất phù hợp cho:</p>



<ul class="wp-block-list">
<li>keyset pagination</li>



<li>multi-column sorting</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="ket-luan" class="wp-block-heading">Kết luận</h2>



<p>Một thay đổi nhỏ:</p>



<ul class="wp-block-list">
<li>bỏ <code>OR</code></li>



<li>dùng tuple comparison</li>
</ul>



<p>=&gt; có thể:</p>



<ul class="wp-block-list">
<li>giảm load database</li>



<li>tránh slow query</li>



<li>đơn giản hoá code</li>
</ul>



<p>Đây là một ví dụ điển hình cho việc:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>tối ưu performance không phải lúc nào cũng cần rewrite lớn — đôi khi chỉ cần hiểu đúng cách database hoạt động.</strong></p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p></p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong/">Tối ưu Slow Query PostgreSQL: Khi một dòng code cứu cả hệ thống</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/toi-uu-slow-query-postgresql-khi-mot-dong-code-cuu-ca-he-thong/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tối ưu SEO cho Laravel Inertia.js với Vue 3 + Docker + Nginx (Phần 2)</title>
		<link>https://blog.tomosia.com.vn/toi-uu-seo-cho-laravel-inertia-js-voi-vue-3-docker-nginx-phan-2/</link>
					<comments>https://blog.tomosia.com.vn/toi-uu-seo-cho-laravel-inertia-js-voi-vue-3-docker-nginx-phan-2/#respond</comments>
		
		<dc:creator><![CDATA[dang cao]]></dc:creator>
		<pubDate>Thu, 12 Mar 2026 07:35:09 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3981</guid>

					<description><![CDATA[<p>Một hướng dẫn thực tế dành cho các dự án Laravel + Inertia.js + Vue 3 chạy trong&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-seo-cho-laravel-inertia-js-voi-vue-3-docker-nginx-phan-2/">Tối ưu SEO cho Laravel Inertia.js với Vue 3 + Docker + Nginx (Phần 2)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Một hướng dẫn thực tế dành cho các dự án Laravel + Inertia.js + Vue 3 chạy trong Docker với Nginx, tập trung vào SEO kỹ thuật (technical SEO) và hiệu năng.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="1-tong-quan-kien-truc" class="wp-block-heading">1. Tổng quan kiến trúc</h2>



<p>Một stack phổ biến hiện nay:</p>



<ul class="wp-block-list">
<li><strong>Backend</strong>: Laravel</li>



<li><strong>Frontend</strong>: Vue 3 thông qua Inertia.js (SPA-like)</li>



<li><strong>Web server</strong>: Nginx</li>



<li><strong>Môi trường</strong>: Docker / Docker Compose</li>
</ul>



<p>Vấn đề lớn nhất với SEO trong mô hình này:</p>



<ul class="wp-block-list">
<li>Nội dung render phía client (CSR)</li>



<li>Bot Google có thể crawl được JS nhưng <strong>chậm</strong> và <strong>không ổn định</strong></li>



<li>Thiếu metadata động</li>



<li>TTFB cao nếu cấu hình Docker/Nginx chưa tối ưu</li>
</ul>



<p>Mục tiêu:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Làm cho website <strong>crawl được – index đúng – load nhanh – metadata đầy đủ</strong>.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="2-hieu-dung-ve-seo-voi-inertia-js" class="wp-block-heading">2. Hiểu đúng về SEO với Inertia.js</h2>



<p>Inertia.js không phải SPA thuần, mà là:</p>



<ul class="wp-block-list">
<li>Server vẫn trả HTML gốc</li>



<li>Vue render phía client</li>
</ul>



<p>Tuy nhiên:</p>



<ul class="wp-block-list">
<li>Nội dung chính vẫn xuất hiện sau khi JS chạy</li>



<li>SEO <strong>chấp nhận được</strong>, nhưng <strong>chưa tối ưu</strong> nếu không cấu hình thêm</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="3-thiet-lap-metadata-dong-cuc-ky-quan-trong" class="wp-block-heading">3. Thiết lập metadata động (cực kỳ quan trọng)</h2>



<h3 id="3-1-dung-head-api-cua-inertia" class="wp-block-heading">3.1 Dùng Head API của Inertia</h3>



<pre class="wp-block-code"><code>import { Head } from '@inertiajs/vue3';</code></pre>



<pre class="wp-block-code"><code>&lt;template>
  &lt;Head>
    &lt;title>{{ title }}&lt;/title>
    &lt;meta name="description" :content="description" />
    &lt;meta property="og:title" :content="title" />
    &lt;meta property="og:description" :content="description" />
  &lt;/Head>
&lt;/template></code></pre>



<h3 id="3-2-truyen-du-lieu-tu-laravel-controller" class="wp-block-heading">3.2 Truyền dữ liệu từ Laravel Controller</h3>



<pre class="wp-block-code"><code>return Inertia::render('Post/Show', &#91;
    'title' => $post->title,
    'description' => Str::limit($post->content, 160),
]);</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="4-render-html-ban-dau-day-du-hon-preload-data" class="wp-block-heading">4. Render HTML ban đầu đầy đủ hơn (Preload data)</h2>



<h3 id="kich-hoat-ssr-server-side-rendering-cho-inertia" class="wp-block-heading">Kích hoạt SSR (Server Side Rendering) cho Inertia</h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Đây là bước giúp SEO <strong>nhảy vọt</strong>.</p>
</blockquote>



<h4 id="cai-ssr-tham-khao-chi-tiet-o-phan-1" class="wp-block-heading">Cài SSR (tham khảo chi tiết ở phần 1)</h4>



<pre class="wp-block-code"><code>npm install @inertiajs/server</code></pre>



<pre class="wp-block-code"><code>php artisan inertia:start-ssr</code></pre>



<p>Laravel sẽ:</p>



<ul class="wp-block-list">
<li>Render HTML phía server</li>



<li>Bot Google thấy ngay nội dung</li>
</ul>



<h4 id="loi-ich" class="wp-block-heading">Lợi ích</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Tiêu chí</th><th>Không SSR</th><th>Có SSR</th></tr></thead><tbody><tr><td>Index</td><td>Chậm</td><td>Nhanh</td></tr><tr><td>Nội dung</td><td>Phụ thuộc JS</td><td>Có sẵn</td></tr><tr><td>SEO score</td><td>Trung bình</td><td>Cao</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="5-cau-hinh-nginx-chuan-seo-hieu-nang" class="wp-block-heading">5. Cấu hình Nginx chuẩn SEO + hiệu năng</h2>



<h3 id="5-1-bat-gzip-brotli" class="wp-block-heading">5.1 Bật gzip / brotli</h3>



<pre class="wp-block-code"><code>gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;</code></pre>



<h3 id="5-2-cache-static-files" class="wp-block-heading">5.2 Cache static files</h3>



<pre class="wp-block-code"><code>location ~* \.(js|css|png|jpg|jpeg|gif|svg|webp)$ {
    expires 30d;
    access_log off;
}</code></pre>



<h3 id="5-3-http-headers-seo" class="wp-block-heading">5.3 HTTP Headers SEO</h3>



<pre class="wp-block-code"><code>add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="6-docker-toi-uu-cho-seo-ttfb-thap" class="wp-block-heading">6. Docker tối ưu cho SEO (TTFB thấp)</h2>



<h3 id="6-1-su-dung-php-fpm-production" class="wp-block-heading">6.1 Sử dụng PHP-FPM production</h3>



<pre class="wp-block-code"><code>FROM php:8.2-fpm-alpine</code></pre>



<h3 id="6-2-opcache" class="wp-block-heading">6.2 OPcache</h3>



<pre class="wp-block-code"><code>opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000</code></pre>



<h3 id="6-3-docker-compose-network-noi-bo" class="wp-block-heading">6.3 Docker Compose network nội bộ</h3>



<p>Tránh expose không cần thiết, giảm latency.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="7-sitemap-robots-txt-tu-dong-trong-laravel" class="wp-block-heading">7. Sitemap &amp; robots.txt tự động trong Laravel</h2>



<h3 id="7-1-cai-package" class="wp-block-heading">7.1 Cài package</h3>



<pre class="wp-block-code"><code>composer require spatie/laravel-sitemap</code></pre>



<h3 id="7-2-tao-sitemap" class="wp-block-heading">7.2 Tạo sitemap</h3>



<pre class="wp-block-code"><code>SitemapGenerator::create(config('app.url'))
    ->writeToFile(public_path('sitemap.xml'));</code></pre>



<h3 id="7-3-robots-txt" class="wp-block-heading">7.3 robots.txt</h3>



<pre class="wp-block-code"><code>User-agent: *
Allow: /
Sitemap: https://domain.com/sitemap.xml</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="8-schema-org-rich-snippets" class="wp-block-heading">8. Schema.org (Rich snippets)</h2>



<p>Thêm JSON-LD trong Inertia Head:</p>



<pre class="wp-block-code"><code>&lt;script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "{{ title }}"
}
&lt;/script></code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="9-kiem-tra-seo" class="wp-block-heading">9. Kiểm tra SEO</h2>



<h3 id="cong-cu-nen-dung" class="wp-block-heading">Công cụ nên dùng</h3>



<ul class="wp-block-list">
<li>Google Search Console</li>



<li>PageSpeed Insights</li>



<li>Lighthouse</li>



<li>Screaming Frog</li>
</ul>



<h3 id="kpi-nen-theo-doi" class="wp-block-heading">KPI nên theo dõi</h3>



<ul class="wp-block-list">
<li>LCP &lt; 2.5s</li>



<li>CLS &lt; 0.1</li>



<li>TTFB &lt; 500ms</li>



<li>Index coverage</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="10-checklist-nhanh" class="wp-block-heading">10. Checklist nhanh</h2>



<p>✅ Inertia Head metadata</p>



<p>✅ SSR enabled</p>



<p>✅ Gzip + cache static</p>



<p>✅ Sitemap.xml</p>



<p>✅ robots.txt</p>



<p>✅ JSON-LD</p>



<p>✅ Docker optimized</p>



<p>✅ OPcache</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 id="11-ket-luan" class="wp-block-heading">11. Kết luận</h2>



<p>Laravel + Inertia.js + Vue 3 hoàn toàn <strong>SEO tốt</strong> nếu:</p>



<ul class="wp-block-list">
<li>Có SSR</li>



<li>Metadata đúng</li>



<li>Server nhanh</li>



<li>Cấu hình Nginx &amp; Docker chuẩn</li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>SEO không chỉ là content – mà là <strong>kiến trúc hệ thống</strong>.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Nếu bạn đang chạy dự án Laravel Inertia trên Docker và muốn audit SEO chuyên sâu (TTFB, crawlability, SSR setup), bạn có thể mở rộng thêm:</p>



<ul class="wp-block-list">
<li>Redis cache</li>



<li>Full page cache cho bot</li>



<li>Edge caching (Cloudflare)</li>



<li>Prerender.io cho bot</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>Chúc bạn tối ưu SEO thành công </p>
<p>The post <a href="https://blog.tomosia.com.vn/toi-uu-seo-cho-laravel-inertia-js-voi-vue-3-docker-nginx-phan-2/">Tối ưu SEO cho Laravel Inertia.js với Vue 3 + Docker + Nginx (Phần 2)</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/toi-uu-seo-cho-laravel-inertia-js-voi-vue-3-docker-nginx-phan-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</title>
		<link>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/</link>
					<comments>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/#respond</comments>
		
		<dc:creator><![CDATA[Nguyen Luan]]></dc:creator>
		<pubDate>Fri, 30 Jan 2026 09:56:08 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Web Server]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=4177</guid>

					<description><![CDATA[<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Hãy tưởng tượng bạn check-in vào một khách sạn 5 sao sang trọng. Hệ thống an ninh tối tân, camera khắp nơi, thẻ từ thang máy hiện đại. Lễ tân đưa cho bạn chìa khóa phòng 101. Bạn lên phòng, mở cửa và nghỉ ngơi. Nhưng sau đó, vì tò mò, bạn thử cạo sửa con số trên chìa khóa thành 102 và cắm vào ổ khóa phòng bên cạnh. <em>Cạch!</em> Cửa mở toang.</p>



<p>Trong thế giới thực, điều này thật nực cười. Nhưng trong thế giới lập trình Web và API, tình huống này xảy ra thường xuyên đến mức nó trở thành &#8220;cơn đau đầu&#8221; kinh niên của giới bảo mật. Nó được gọi là <strong>IDOR</strong> (Insecure Direct Object Reference) hay <strong>BOLA</strong> (Broken Object Level Authorization).</p>



<p>Nếu bạn là một Backend Developer, và bạn tin rằng chỉ cần có <mark style="background-color:#e9ecef" class="has-inline-color"><code>Authentication</code> </mark>(Đăng nhập) là hệ thống đã an toàn, thì bài viết này sẽ thay đổi tư duy lập trình của bạn. Chúng ta sẽ không chỉ nói về bề nổi, mà sẽ đi sâu vào bản chất kiến trúc của lỗi này.</p>



<h3 id="1-ban-chat-ky-thuat-cua-idor" class="wp-block-heading">1. Bản chất kỹ thuật của IDOR</h3>



<p><strong>IDOR</strong> (Insecure Direct Object Reference) không chỉ là một lỗi code, nó là một <strong>lỗi về thiết kế Logic (Business Logic Flaw)</strong>.</p>



<p>Trong danh sách <strong>OWASP API Security Top 10</strong>, IDOR chính là bản chất của lỗ hổng số 1: <strong><a href="https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/">API1: 2023 Broken Object Level Authorization (BOLA)</a></strong>.</p>



<p>Hiểu sâu hơn, IDOR xảy ra khi 3 điều kiện sau hội tụ:</p>



<ol class="wp-block-list">
<li><strong>Direct Reference:</strong> Ứng dụng để lộ mã định danh nội bộ (Internal Identifier) của đối tượng (như Database Primary Key, Filename) trực tiếp ra ngoài URL hoặc Body request.</li>



<li><strong>User Input:</strong> Ứng dụng tin tưởng và sử dụng trực tiếp đầu vào này để truy vấn dữ liệu.</li>



<li><strong>Missing Authorization:</strong> Ứng dụng bỏ qua bước kiểm tra quyền truy cập <em>tại thời điểm</em> truy xuất đối tượng.</li>
</ol>



<p><strong>Sai lầm phổ biến:</strong> Nhiều Dev nhầm lẫn giữa <em>Authentication</em> (Bạn là ai?) và <em>Authorization</em> (Bạn được làm gì?). IDOR xảy ra khi bạn đã qua cửa Authentication, nhưng hệ thống quên kiểm tra Authorization ở mức độ đối tượng (Object-level).</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="559" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png" alt="" class="wp-image-4187" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1024x559.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-300x164.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-768x419.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-380x207.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-800x436.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1-1160x633.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_eohbxheohbxheohb-1.png 1408w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="2-giai-phau-co-che-tan-cong" class="wp-block-heading">2. Giải phẫu cơ chế tấn công</h3>



<p>Hãy mổ xẻ một HTTP Request để thấy IDOR có thể lẩn trốn ở đâu. Không chỉ ở URL Parameter, nó có thể nằm ở Header hoặc Body.</p>



<p><strong>Kịch bản:</strong> Ứng dụng Chat cho phép người dùng tải lại lịch sử chat.</p>



<p><strong>Request bình thường:</strong></p>



<pre class="wp-block-code"><code>GET /api/v1/messages/history HTTP/1.1
Host: example.com
Cookie: session_id=abc123xyz...
X-User-ID: 1005  &lt;-- IDOR tiềm ẩn ở Header</code></pre>



<ol class="wp-block-list">
<li><strong>Hacker quan sát:</strong> Thấy Header <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID: 1005</mark></code> được gửi kèm mỗi request.</li>



<li><strong>Thử nghiệm:</strong> Hacker dùng Postman hoặc Burp Suite để chặn request, sửa <code><mark style="background-color:#e9ecef" class="has-inline-color">X-User-ID</mark></code> thành <code>1006</code>.</li>



<li><strong>Kết quả:</strong> Server, thay vì lấy ID từ session an toàn (Server-side), lại tin tưởng lấy ID từ Header (Client-side) để query database <code><mark style="background-color:#e9ecef" class="has-inline-color">SELECT * FROM messages WHERE user_id = 1006</mark></code>.</li>



<li><strong>Hậu quả:</strong> Hacker đọc được toàn bộ tin nhắn của người dùng 1006.</li>
</ol>



<p><em><strong>IDOR có thể xuất hiện ở:</strong></em></p>



<ul class="wp-block-list">
<li><strong>URL Path:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/users/100/profile</mark></code></li>



<li><strong>Query Params:</strong> <code><mark style="background-color:#e9ecef" class="has-inline-color">/invoices?id=100</mark></code></li>



<li><strong>Body (JSON/XML):</strong> <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/change-password</code> với body <code>{"user_id": 100, "new_pass": "..."}</code></mark></li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png" alt="" class="wp-image-4190" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_t2k171t2k171t2k1-1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 id="3-tai-sao-idor-lai-la-sat-thu-tham-lang" class="wp-block-heading">3. Tại sao IDOR lại là &#8220;Sát thủ thầm lặng&#8221;?</h3>



<p>IDOR nguy hiểm vì tính &#8220;hợp lệ&#8221; về mặt cú pháp của nó.</p>



<ul class="wp-block-list">
<li><strong>Vượt mặt WAF (Web Application Firewall):</strong> Các tường lửa thường chặn các ký tự lạ như <code><mark style="background-color:#e9ecef" class="has-inline-color">' OR 1=1 --</mark></code> (SQL Injection) hay <code><mark style="background-color:#e9ecef" class="has-inline-color">&lt;script&gt;</mark></code> (XSS). Nhưng với IDOR, payload chỉ là một con số: <code>id=1006</code>. Đối với WAF, đây là một con số hoàn toàn sạch và hợp lệ.</li>



<li><strong>Khó phát hiện bằng Automation Tool:</strong> Các máy quét (Scanner) không hiểu ngữ cảnh nghiệp vụ. Nếu máy quét gửi <code><mark style="background-color:#e9ecef" class="has-inline-color">id=1006</mark></code> và nhận về <code>200 OK</code>, nó đánh dấu là &#8220;Thành công&#8221;. Nó không biết rằng dữ liệu trả về đó <em>không thuộc về</em> người đang gửi request. Chỉ có con người (Pentesters) hoặc Unit Test được viết kỹ lưỡng mới phát hiện ra.</li>



<li><strong>Hệ quả dây chuyền:</strong> IDOR thường là bước đệm để leo thang đặc quyền (Privilege Escalation), dẫn đến việc chiếm đoạt toàn bộ hệ thống (Account Takeover).</li>
</ul>



<p>Sự nguy hiểm của IDOR nằm ở chỗ nó là một lỗ hổng logic, không phải lỗ hổng cú pháp. Các công cụ quét bảo mật tĩnh (SAST) truyền thống thường thất bại trong việc phát hiện IDOR vì chúng không thể hiểu ngữ cảnh &#8220;ai sở hữu cái gì&#8221; trong một ứng dụng cụ thể. Điều này dẫn đến một nghịch lý: trong khi các lỗ hổng kỹ thuật như <strong>SQL Injection</strong> đang giảm dần nhờ các framework hiện đại, IDOR vẫn tồn tại dai dẳng và chiếm tỷ lệ cao trong các chương trình săn lỗi nhận thưởng (Bug Bounty), đặc biệt là trong các lĩnh vực y tế, chính phủ và dịch vụ chuyên nghiệp.</p>



<h3 id="4-phan-loai-idor-chuyen-sau" class="wp-block-heading">4. Phân loại IDOR chuyên sâu.</h3>



<h4 id="4-1-leo-thang-dac-quyen-ngang-horizontal-escalation" class="wp-block-heading">4.1. Leo thang đặc quyền ngang (Horizontal Escalation)</h4>



<p>Đây là dạng phổ biến nhất. Người dùng A truy cập dữ liệu của Người dùng B có cùng vai trò.</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong> Xem đơn hàng, tải hóa đơn, xem ảnh riêng tư.</li>



<li><strong>Rủi ro:</strong> Rò rỉ thông tin cá nhân (PII Breach), vi phạm GDPR/CCPA.</li>
</ul>



<h4 id="4-2-leo-thang-dac-quyen-doc-vertical-escalation" class="wp-block-heading">4.2. Leo thang đặc quyền dọc (Vertical Escalation)</h4>



<p>Người dùng thường (User) tìm cách gọi các API dành riêng cho Quản trị viên (Admin/Manager).</p>



<ul class="wp-block-list">
<li><strong>Ví dụ:</strong>
<ul class="wp-block-list">
<li>Hacker tìm thấy API: <mark style="background-color:#e9ecef" class="has-inline-color"><code>POST /api/users/1001/roles</code>.</mark></li>



<li>Hacker gửi request với body: <code><mark style="background-color:#e9ecef" class="has-inline-color">{"role": "ADMIN"}</mark></code>.</li>



<li>Nếu Backend không check xem &#8220;người gọi API này có phải là Super Admin không&#8221;, Hacker lập tức trở thành Admin.</li>
</ul>
</li>
</ul>



<h4 id="4-3-idor-tren-file-tinh-static-file-idor" class="wp-block-heading">4.3. IDOR trên File tĩnh (Static File IDOR)</h4>



<p>Tên file thường dễ đoán.</p>



<ul class="wp-block-list">
<li>Ví dụ: <code><mark style="background-color:#e9ecef" class="has-inline-color">https://example.com/uploads/receipts/receipt_001.pdf.</mark></code></li>



<li>Hacker viết script lặp từ <code>001</code> đến <code>9999</code> để tải toàn bộ hóa đơn của hệ thống.</li>
</ul>



<h3 id="5-dan-chung-thuc-te-khi-ong-lon-cung-mac-sai-lam" class="wp-block-heading">5. Dẫn chứng thực tế: Khi &#8220;Ông lớn&#8221; cũng mắc sai lầm</h3>



<h4 id="5-1-mcdonalds-su-co-mchire-7-2025" class="wp-block-heading">5.1. McDonald&#8217;s &amp; sự cố McHire (7/2025)</h4>



<p>Link: <a href="https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/">https://research.cgu.edu/icdc/2025/07/01/mcdonalds-july-2025-breach/</a></p>



<p><strong>Bối cảnh</strong>: Hệ thống bị ảnh hưởng là&nbsp;<strong>McHire</strong>, nền tảng tuyển dụng toàn cầu của McDonald&#8217;s, được vận hành bởi đối tác công nghệ bên thứ ba là&nbsp;<strong>Paradox.ai</strong>. Paradox.ai cung cấp giải pháp chatbot AI tên là &#8220;Olivia&#8221; để tự động hóa quy trình phỏng vấn, thu thập thông tin ứng viên và lên lịch làm việc. Sự phụ thuộc vào bên thứ ba này đã mở rộng bề mặt tấn công của McDonald&#8217;s ra ngoài hạ tầng kiểm soát trực tiếp của họ.<sup></sup>&nbsp;&nbsp;&nbsp;</p>



<p><strong>Nguyên nhân chính</strong><br>Sự cố xảy ra do kết hợp 2 lỗi nghiêm trọng:</p>



<ol class="wp-block-list">
<li><strong>Xác thực yếu (Weak Authentication)</strong>
<ul class="wp-block-list">
<li>Một tài khoản admin thử nghiệm của Paradox.ai dùng mật khẩu <strong>“123456”</strong>, không có MFA.</li>



<li>Tài khoản này tồn tại từ <strong>2019</strong>, không bị vô hiệu hóa dù có quyền truy cập production.</li>
</ul>
</li>



<li><strong>Lỗ hổng IDOR (Missing Object Level Authorization)</strong>
<ul class="wp-block-list">
<li>API backend cho phép truy xuất hồ sơ ứng viên bằng <code><mark style="background-color:#e9ecef" class="has-inline-color">applicant_id</mark></code>.</li>



<li>Không kiểm tra quyền truy cập → chỉ cần đổi ID là đọc được dữ liệu của bất kỳ ứng viên nào.</li>



<li>Từ 1 tài khoản → mở rộng ra <strong>64 triệu hồ sơ toàn cầu</strong>.</li>
</ul>
</li>
</ol>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ <strong>PII</strong>: tên, email, số điện thoại, địa chỉ nhà.</li>



<li>Lộ dữ liệu nhạy cảm: log chat với AI (tính cách, sở thích, thông tin cá nhân).</li>



<li>Lộ token phiên → có thể giả danh ứng viên.</li>



<li>Nguy cơ cao cho <strong>social engineering, lừa đảo, chiếm tài khoản</strong>.</li>
</ul>



<p>Sự kết hợp giữa <strong>quản lý tài khoản kém</strong> (tài khoản ma, không MFA) và <strong>thiếu kiểm soát quyền API</strong> (IDOR) tạo ra “cơn bão hoàn hảo” cho rò rỉ dữ liệu quy mô toàn cầu – một xu hướng cực kỳ đáng báo động trong năm 2025</p>



<h4 id="5-2-mozilla-firefox-accounts-lo-hong-xoa-tai-khoan-5-2025" class="wp-block-heading">5.2. Mozilla Firefox Accounts – Lỗ hổng xóa tài khoản (5/2025)</h4>



<p>Link: <a href="https://hackerone.com/reports/3154983">https://hackerone.com/reports/3154983</a></p>



<p><strong>Bản chất sự cố</strong></p>



<ul class="wp-block-list">
<li>Lỗ hổng IDOR nghiêm trọng trong API xóa tài khoản (<code><mark style="background-color:#e9ecef" class="has-inline-color">/v1/account/destroy</mark></code>).</li>



<li>Cho phép <strong>xóa vĩnh viễn tài khoản người khác</strong> mà không cần tương tác từ nạn nhân (zero-click).</li>



<li>Ảnh hưởng trực tiếp đến Integrity (toàn vẹn) và Availability (sẵn sàng) của dữ liệu.</li>
</ul>



<p><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>Lỗi Session Misbinding: backend không kiểm tra phiên làm việc có khớp với tài khoản bị xóa hay không.</li>



<li>API tin vào payload JSON (email, authPW) thay vì session của người gửi request.</li>



<li>Đây là dạng IDOR ghi/xóa, nguy hiểm hơn IDOR đọc dữ liệu.</li>
</ul>



<p><strong>Kịch bản khai thác</strong></p>



<ol class="wp-block-list">
<li>Kẻ tấn công tạo tài khoản Firefox, đăng nhập bằng SSO (Google).</li>



<li>Chặn request xóa tài khoản của chính mình.</li>



<li>Đổi email trong payload thành email nạn nhân (cũng dùng SSO, chưa set mật khẩu).</li>



<li>Gửi request → hệ thống xóa tài khoản nạn nhân.</li>
</ol>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Mất vĩnh viễn dữ liệu: mật khẩu đã lưu, lịch sử duyệt web, tab, sync data.</li>



<li>Không thể khôi phục, không cần nạn nhân click hay xác nhận.</li>



<li>Được đánh giá <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><strong>Critical </strong></mark>trên HackerOne.</li>
</ul>



<p><strong>Bài học cốt lõi</strong>: IDOR không chỉ gây rò rỉ dữ liệu (read) mà còn có thể phá hủy dữ liệu (write/delete) nếu backend không ràng buộc chặt chẽ giữa session và đối tượng tài nguyên.</p>



<h4 id="5-3-gitlab-lo-hong-machine-learning-model-registry-2024-2025" class="wp-block-heading">5.3. GitLab – Lỗ hổng Machine Learning Model Registry (2024–2025)</h4>



<p>Link: <a href="https://hackerone.com/reports/2528293">https://hackerone.com/reports/2528293</a></p>



<p><strong>Bối cảnh</strong></p>



<ul class="wp-block-list">
<li>GitLab triển khai Machine Learning Model Registry để quản lý phiên bản mô hình AI.</li>



<li>Lỗ hổng được báo cáo từ 5/2024, vá và công bố kéo dài sang <strong>2025</strong>, cho thấy độ phức tạp của bảo mật trên nền tảng lớn.</li>



<li>Sự cố ảnh hưởng trực tiếp đến tài sản trí tuệ AI (model weights, biases).</li>
</ul>



<p><strong>Nguyên nhân kỹ thuật</strong></p>



<ul class="wp-block-list">
<li>GitLab dùng <strong>ID tịnh tiến (1,2,3,&#8230;)</strong> cho <mark style="background-color:#e9ecef" class="has-inline-color"><code>model_id</code> </mark>thay vì UUID ngẫu nhiên.</li>



<li>API tải/xem mô hình không kiểm tra quyền truy cập dự án (Missing Object Level Authorization).</li>



<li>Đây là IDOR điển hình + thiết kế API kém an toàn.</li>
</ul>



<p><strong>Cách khai thác</strong></p>



<ul class="wp-block-list">
<li>Kẻ tấn công chỉ cần tài khoản GitLab miễn phí.</li>



<li>Lặp ID từ 1 → hàng triệu để liệt kê và tải mô hình của dự án khác.</li>



<li>Không cần brute-force, không cần quyền dự án.</li>
</ul>



<p><strong>Tác động</strong></p>



<ul class="wp-block-list">
<li>Rò rỉ mô hình ML độc quyền của doanh nghiệp.</li>



<li>Nguy cơ mất bí mật thương mại, thiệt hại kinh tế lớn trong bối cảnh AI là tài sản cốt lõi.</li>



<li>Lỗ hổng có thể bị khai thác tự động, quy mô lớn (enumeration).</li>
</ul>



<p><strong>Bài học cốt lõi</strong>: Không dùng ID tịnh tiến cho API công khai. Nó biến IDOR từ lỗ hổng tiềm ẩn thành thảm họa có thể khai thác hàng loạt, đặc biệt nguy hiểm với tài sản AI.</p>



<h3 id="6-ngan-chan-idor-hieu-qua-best-practices" class="wp-block-heading">6. Ngăn chặn IDOR hiệu quả (Best Practices)</h3>



<p>Khi một tài khoản test có thể mở khóa 64 triệu hồ sơ, một email có thể xóa tài khoản người khác, và một con số tăng dần có thể làm bay cả mô hình AI, thì rõ ràng vấn đề không nằm ở “hacker quá giỏi” — mà ở chỗ chúng ta cần chặn IDOR cho đúng cách. </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="572" src="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png" alt="" class="wp-image-4197" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1024x572.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-300x167.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-768x429.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-380x212.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-800x447.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1-1160x647.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2026/01/Image_61mhmz61mhmz61mh1.png 1376w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p><em>&#8220;Đừng bao giờ tin tưởng Client&#8221;</em>. Dưới đây là chiến lược phòng thủ đa lớp <strong>(Defense in Depth)</strong>.</p>



<p><strong>Lớp 1: Kiểm soát truy cập tại tầng Data (Data-Layer Authorization)</strong></p>



<p>Đây là chốt chặn cuối cùng và quan trọng nhất. Mọi câu lệnh truy vấn liên quan đến dữ liệu người dùng <strong>phải</strong> kèm theo điều kiện về chủ sở hữu.</p>



<p><strong>❌ Code rủi ro (Spring Data JPA):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Controller
public Order getOrder(@PathVariable Long id) {
    return orderRepo.findById(id); // Chỉ tìm theo ID đơn hàng
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Controller</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id); </span><span style="color: #6A9955">// Chỉ tìm theo ID đơn hàng</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><strong>✅ Code an toàn (Repository Pattern):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:15.40625px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="// Repository
// Luôn gắn kèm userId vào câu query
@Query(&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;)
Optional&lt;Order&gt; findByIdAndOwner(@Param(&quot;id&quot;) Long id, @Param(&quot;currentUserId&quot;) Long userId);

// Service Layer
public Order getOrder(Long id, User currentUser) {
    return orderRepo.findByIdAndOwner(id, currentUser.getId())
        .orElseThrow(() -&gt; new ResourceNotFoundException(&quot;Order not found or access denied&quot;));
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// Repository</span></span>
<span class="line"><span style="color: #6A9955">// Luôn gắn kèm userId vào câu query</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">Query</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;SELECT o FROM Order o WHERE o.id = :id AND o.owner.id = :currentUserId&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #4EC9B0">Optional</span><span style="color: #D4D4D4">&lt;Order&gt; </span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;id&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, @</span><span style="color: #4EC9B0">Param</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;currentUserId&quot;</span><span style="color: #D4D4D4">) </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> userId);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// Service Layer</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Order</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getOrder</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id, </span><span style="color: #4EC9B0">User</span><span style="color: #D4D4D4"> currentUser) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">orderRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findByIdAndOwner</span><span style="color: #D4D4D4">(id, </span><span style="color: #9CDCFE">currentUser</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getId</span><span style="color: #D4D4D4">())</span></span>
<span class="line"><span style="color: #D4D4D4">        .</span><span style="color: #DCDCAA">orElseThrow</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ResourceNotFoundException</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Order not found or access denied&quot;</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><strong>Lớp 2: Kiểm soát truy cập tại tầng Controller (Method-Level Security)</strong></p>



<p>Sử dụng các Annotation bảo mật của Framework (như Spring Security) để chặn ngay từ cửa vào.</p>



<p><strong>✅ Ví dụ với Spring Security (<code><mark style="background-color:#e9ecef" class="has-inline-color">@PreAuthorize</mark></code>):</strong></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:7.703125px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">Java</span><span role="button" tabindex="0" data-code="@GetMapping(&quot;/invoices/{id}&quot;)
// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này
@PreAuthorize(&quot;@securityService.isOwner(authentication, #id)&quot;) 
public Invoice getInvoice(@PathVariable Long id) {
    return invoiceRepo.findById(id);
}" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">GetMapping</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/invoices/{id}&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #6A9955">// SpEL: Chỉ cho phép nếu user hiện tại là chủ sở hữu của invoice có id này</span></span>
<span class="line"><span style="color: #D4D4D4">@</span><span style="color: #4EC9B0">PreAuthorize</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;@securityService.isOwner(authentication, #id)&quot;</span><span style="color: #D4D4D4">) </span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Invoice</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getInvoice</span><span style="color: #D4D4D4">(@</span><span style="color: #4EC9B0">PathVariable</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Long</span><span style="color: #D4D4D4"> id) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">invoiceRepo</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">findById</span><span style="color: #D4D4D4">(id);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p><em>Cách này giúp code business logic sạch hơn, tách biệt logic bảo mật ra khỏi logic nghiệp vụ.</em></p>



<p><strong>Lớp 3: Làm khó Hacker bằng UUID (Obfuscation)</strong></p>



<p>Thay vì dùng Auto-Increment Integer (<code>1, 2, 3</code>), hãy dùng <strong>UUID</strong> (Random String: <code><mark style="background-color:#e9ecef" class="has-inline-color">a0eebc99-9c0b...</mark></code>) hoặc các thuật toán băm ID (như Hashids).</p>



<ul class="wp-block-list">
<li><strong>Lợi ích:</strong> Chặn đứng tấn công kiểu liệt kê (Enumeration Attack). Hacker không thể viết vòng lặp <code><mark style="background-color:#e9ecef" class="has-inline-color">for (i=0; i&lt;1000; i++)</mark></code> để quét dữ liệu.</li>



<li><strong>Cảnh báo:</strong> UUID <strong>KHÔNG THỂ THAY THẾ</strong> được <em>Lớp 1</em> và <em>Lớp 2</em>. Nếu hacker bằng cách nào đó biết được UUID của nạn nhân, IDOR vẫn xảy ra nếu thiếu Authorization check. UUID chỉ là &#8220;lớp sương mù&#8221;, không phải &#8220;bức tường thép&#8221;.</li>
</ul>



<p><strong>Lớp 4: Kiểm thử tự động (Automated Security Testing)</strong></p>



<p>Đừng đợi Pentester tìm lỗi. Hãy viết Test Case cho IDOR.</p>



<ul class="wp-block-list">
<li><strong>Unit Test:</strong> Tạo 2 user (Alice, Bob). Đăng nhập bằng Alice, cố gắng request dữ liệu ID của Bob. Assert rằng kết quả trả về phải là <code><mark style="background-color:#e9ecef" class="has-inline-color">403 Forbidden</mark></code> hoặc <code><mark style="background-color:#e9ecef" class="has-inline-color">404 Not Found</mark></code>.</li>
</ul>



<h3 id="7-ket-luan" class="wp-block-heading">7. Kết luận</h3>



<p>IDOR không phải là một lỗi kỹ thuật phức tạp, nhưng nó phản ánh sự thiếu sót trong tư duy thiết kế hệ thống.</p>



<p>Hãy nhớ nguyên tắc vàng: <strong>Mọi request truy cập dữ liệu đều phải trả lời được hai câu hỏi:</strong></p>



<ol class="wp-block-list">
<li><em>Người dùng này là ai? (Authentication)</em></li>



<li><em>Dữ liệu này có phải của họ không? (Authorization)</em></li>
</ol>



<p>Nếu thiếu câu hỏi số 2, chúng ta đang mở toang cánh cửa &#8220;phòng 102&#8221; cho bất kỳ ai cầm chìa khóa &#8220;phòng 101&#8221;.</p>



<p></p>
<p>The post <a href="https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/">IDOR &#8211; Lỗ hổng &#8220;ngây thơ&#8221; nhưng chí mạng trong kiến trúc phần mềm?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/idor-lo-hong-ngay-tho-nhung-chi-mang-trong-kien-truc-phan-mem/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel Scheduler: Tư duy thiết kế, trade-off và best practices cho hệ thống lớn</title>
		<link>https://blog.tomosia.com.vn/laravel-scheduler-tu-duy-thiet-ke-trade-off-va-best-practices-cho-he-thong-lon/</link>
					<comments>https://blog.tomosia.com.vn/laravel-scheduler-tu-duy-thiet-ke-trade-off-va-best-practices-cho-he-thong-lon/#respond</comments>
		
		<dc:creator><![CDATA[hoa nguyen]]></dc:creator>
		<pubDate>Thu, 25 Dec 2025 09:26:41 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[cronjob]]></category>
		<category><![CDATA[Scheduler]]></category>
		<category><![CDATA[Laravel]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3967</guid>

					<description><![CDATA[<p>Nếu bạn đã đi làm vài năm, bạn sẽ thấy &#8220;Scheduler&#8221; không phải chuyện&#160;viết vài dòng&#160;dailyAt(). Nó là&#160;bài&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/laravel-scheduler-tu-duy-thiet-ke-trade-off-va-best-practices-cho-he-thong-lon/">Laravel Scheduler: Tư duy thiết kế, trade-off và best practices cho hệ thống lớn</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Nếu bạn đã đi làm vài năm, bạn sẽ thấy &#8220;Scheduler&#8221; không phải chuyện&nbsp;<em>viết vài dòng&nbsp;<code>dailyAt()</code></em>. Nó là&nbsp;<strong>bài toán reliability + performance + observability</strong>. Sai một ly là: trùng charge, spam notify, kẹt DB lúc 9h, hoặc &#8220;đêm qua không chạy mà không ai biết&#8221;.</p>



<p>Bài này chia sẻ theo góc nhìn hệ thống lớn: <strong>ra quyết định kỹ thuật</strong> khi số job tăng nhanh, dữ liệu lớn, nhiều worker, CI/CD liên tục, thậm chí realtime.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="1-nguyên-tắc-số-1-scheduler-phải-nhẹ--việc-nặng-đẩy-qua-queue"><span id="1-nguyen-tac-so-1-scheduler-phai-nhe-viec-nang-day-qua-queue">1. Nguyên tắc số 1: Scheduler phải &#8220;nhẹ&#8221; — việc nặng đẩy qua queue</span></h2>



<p><strong>Tư duy</strong>: Scheduler là &#8220;orchestrator&#8221;, không phải &#8220;executor&#8221;.</p>



<p>Scheduler chỉ làm 2 việc:</p>



<ul class="wp-block-list">
<li>Quyết định &#8220;đúng giờ thì trigger cái gì&#8221;</li>



<li>Dispatch nhanh, ghi dấu vết (batch metadata)</li>
</ul>



<p>Việc nặng (DB scan, gọi API, gửi notify hàng loạt, reconcile, billing…) → chạy trong&nbsp;<strong>queued jobs</strong>&nbsp;+ nhiều worker.</p>



<p><strong>Trade-off</strong>:</p>



<ul class="wp-block-list">
<li>✅ Ưu: Scale bằng worker, retry/backoff chuẩn, tách tải khỏi scheduler</li>



<li>⚠️ Nhược: Job có thể chạy trễ → phải thiết kế theo &#8220;batch time&#8221; (xem phần 4)</li>
</ul>



<p><strong>Sai lầm thường gặp</strong>: Nhét logic nặng vào scheduler (closure/command) rồi &#8220;đỡ nghẽn&#8221; bằng cách tăng CPU. Đến lúc 100 lịch/ngày thì scheduler thành single-point bottleneck.</p>



<p><strong>Ví dụ</strong>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

// ❌ SAI: Logic nặng trực tiếp trong scheduler
Schedule::call(function () {
    DB::table('recent_users')->chunk(100, function ($users) {
        foreach ($users as $user) {
            // Process payment, send email, update DB...
            // Nếu có 1000 users, scheduler sẽ bị block rất lâu
        }
    });
})->dailyAt('09:00');

// ✅ ĐÚNG: Scheduler chỉ dispatch job
Schedule::job(new ProcessRecentUsersJob)
    ->dailyAt('09:00');</code></pre>



<p>Scheduler chỉ trigger đúng giờ, không bao giờ chạy logic business trực tiếp. Worker sẽ xử lý job trong queue.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="2-đúng-một-lần-theo-nghĩa-business--đúng-một-lần-theo-nghĩa-kỹ-thuật"><span id="2-dung-mot-lan-theo-nghia-business-dung-mot-lan-theo-nghia-ky-thuat">2. &#8220;Đúng một lần&#8221; theo nghĩa business &gt; &#8220;đúng một lần&#8221; theo nghĩa kỹ thuật</span></h2>



<p>Ở hệ thống lớn, &#8220;at least once&#8221; là mặc định. Scheduler/queue/worker/retry/deploy đều có thể làm job chạy lại.</p>



<p><strong>Best practice</strong>: Thiết kế job theo&nbsp;<strong>idempotent</strong>:</p>



<ul class="wp-block-list">
<li>Chạy 2 lần vẫn ra kết quả đúng</li>



<li>Side-effect (charge/send/update) phải có khóa logic hoặc unique key</li>
</ul>



<p>Dùng lock ở&nbsp;<strong>đúng tầng</strong>:</p>



<ul class="wp-block-list">
<li>Tầng scheduler: Chống chồng / chạy một server (khi HA)</li>



<li>Tầng job/business: Lock theo entity/time-window (mới là thứ bảo vệ cuối)</li>
</ul>



<p><strong>Trade-off</strong>:</p>



<ul class="wp-block-list">
<li>Lock nhiều quá → Giảm throughput, dễ nghẽn nếu lock sai granularity</li>



<li>Lock ít quá → Trùng dữ liệu/side-effect</li>
</ul>



<p><strong>Sai lầm thường gặp</strong>: Tin tuyệt đối vào &#8220;chống chồng&#8221; của scheduler rồi bỏ qua idempotency. Một ngày deploy &#8220;đúng lúc&#8221;, bạn sẽ hiểu.</p>



<p><strong>Ví dụ</strong>:</p>



<p>Job có idempotency key dựa trên&nbsp;<code>entity_id + batch_time</code>. Concept này áp dụng cho mọi job có side-effect quan trọng. Trong scheduler, bạn chỉ dispatch job với metadata:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::job(new ProcessOrdersJob)
    ->dailyAt('09:00');</code></pre>



<p>Job sẽ tự implement idempotency check trong&nbsp;<code>handle()</code>&nbsp;method để đảm bảo không xử lý trùng.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="3-khi-nào-dùng-runinbackground-và-khi-nào-bắt-buộc-dùng-queue"><span id="3-khi-nao-dung-runinbackground-va-khi-nao-bat-buoc-dung-queue">3. Khi nào dùng&nbsp;<code>runInBackground()</code>&nbsp;và khi nào &#8220;bắt buộc&#8221; dùng queue?</span></h2>



<p>Tôi coi&nbsp;<code>runInBackground()</code>&nbsp;là&nbsp;<strong>giải pháp tactical</strong>, không phải chiến lược lâu dài.</p>



<p><strong>Dùng&nbsp;<code>runInBackground()</code>&nbsp;khi</strong>:</p>



<ul class="wp-block-list">
<li>Task là command/exec tương đối ngắn</li>



<li>Không cần scale theo tải</li>



<li>Bạn chỉ cần tránh scheduler bị giữ process quá lâu</li>
</ul>



<p><strong>Bắt buộc đưa qua queue khi</strong>:</p>



<ul class="wp-block-list">
<li>Task có thể chạy vài chục giây/phút</li>



<li>Task có thể &#8220;nở&#8221; theo dữ liệu (data grows)</li>



<li>Task có side-effect quan trọng (payment/notify hàng loạt)</li>



<li>Cần retry/backoff, rate-limit, phân ưu tiên</li>
</ul>



<p><strong>Sai lầm thường gặp</strong>: &#8220;Task đang lâu → thêm&nbsp;<code>runInBackground()</code>&#8221; và coi như xong. Thực tế bạn chỉ chuyển nghẽn từ scheduler sang CPU/DB/connections của cùng máy.</p>



<p><strong>Ví dụ</strong>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// ✅ Dùng runInBackground cho command nhẹ, không cần scale
Schedule::command('cache:clear')
    ->hourly()
    ->runInBackground();

Schedule::command('emails:send')
    ->dailyAt('02:00')
    ->runInBackground();

// ✅ Bắt buộc dùng queue cho job có side-effect quan trọng
Schedule::job(new SendOrderConfirmationJob)
    ->dailyAt('09:00'); // Không có runInBackground, vì đã là queued job</code></pre>



<p>Chỉ có 2 loại command dùng&nbsp;<code>runInBackground()</code>:</p>



<ol class="wp-block-list">
<li><strong>Maintenance commands</strong> (cleanup, cache) — không ảnh hưởng business</li>



<li><strong>Monitoring commands</strong> — cần chạy nhanh, không cần retry</li>
</ol>



<p>Tất cả job business đều qua queue để có retry/backoff/scale.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="4-bài-toán-lớn-nhất-của-hệ-queue-job-chạy-trễ--lệch-cửa-sổ-dữ-liệu"><span id="4-bai-toan-lon-nhat-cua-he-queue-job-chay-tre-%e2%86%92-lech-cua-so-du-lieu">4. Bài toán lớn nhất của hệ queue: Job chạy trễ → lệch cửa sổ dữ liệu</span></h2>



<p>Đây là điểm phân biệt hệ &#8220;chạy được&#8221; và hệ &#8220;chạy đúng&#8221;.</p>



<p><strong>Vấn đề</strong>:</p>



<ul class="wp-block-list">
<li>Scheduler trigger 09:00, worker chạy 09:04</li>



<li>Nếu job query theo <code>now()</code> lúc 09:04, bạn vừa:
<ul class="wp-block-list">
<li>Miss dữ liệu 09:00–09:04 (tùy window)</li>



<li>Hoặc duplicate với batch trước</li>
</ul>
</li>
</ul>



<p><strong>Giải pháp</strong>: Scheduler&nbsp;<strong>đóng dấu batch time</strong>&nbsp;ngay lúc dispatch (tôi hay gọi&nbsp;<code>scheduledAt</code>/<code>batchAt</code>). Job luôn dùng&nbsp;<code>scheduledAt</code>&nbsp;để:</p>



<ul class="wp-block-list">
<li>Tính window dữ liệu</li>



<li>Log/audit theo batch</li>



<li>Tạo idempotency key</li>
</ul>



<p><strong>Trade-off</strong>:</p>



<ul class="wp-block-list">
<li>Tốn thêm metadata, thêm &#8220;kỷ luật&#8221; trong code</li>



<li>Đổi lại: Batch chạy trễ vẫn <strong>đúng cửa sổ</strong>, dễ đối soát, dễ replay theo batch</li>
</ul>



<p><strong>Ví dụ — Pattern batch timestamp</strong>:</p>



<p>Concept: Scheduler đóng dấu batch time ngay lúc dispatch. Job nhận batch time và dùng để tính time window, không dùng&nbsp;<code>now()</code>.</p>



<p><strong>Sử dụng trong Laravel 12</strong>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::job(new ProcessOrdersJob)
    ->dailyAt('09:00');</code></pre>



<p>Job sẽ nhận&nbsp;<code>scheduledAt</code>&nbsp;từ scheduler event và dùng để query đúng time window.</p>



<p><strong>Ví dụ với sub-minute scheduling trong Laravel 12</strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#sub-minute-scheduled-tasks">Laravel 12.x Sub-Minute Scheduled Tasks</a>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// Laravel 12 hỗ trợ sub-minute scheduling
Schedule::call(function () {
    // Process tasks every 30 seconds
})->everyThirtySeconds();</code></pre>



<p><strong>Lợi ích</strong>:</p>



<ul class="wp-block-list">
<li>Job chạy trễ 3 phút vẫn query đúng cửa sổ 09:00–09:01</li>



<li>Dễ đối soát: &#8220;Batch 09:00 đã xử lý bao nhiêu records?&#8221;</li>



<li>Dễ replay: &#8220;Chạy lại batch 09:00&#8221; → chỉ cần tạo job với <code>jobCreatedAt = 09:00</code></li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="5-tách-workload-shape-phân-queue-theo-ưu-tiên-tránh-job-rác-ăn-tài-nguyên-job-sống-còn"><span id="5-tach-workload-shape-phan-queue-theo-uu-tien-tranh-job-rac-an-tai-nguyen-job-song-con">5. Tách &#8220;workload shape&#8221;: Phân queue theo ưu tiên, tránh job &#8220;rác&#8221; ăn tài nguyên job &#8220;sống còn&#8221;</span></h2>



<p>Một hệ thống lớn sẽ có job kiểu:</p>



<ul class="wp-block-list">
<li><strong>Critical</strong>: Billing, payment, cancel, inventory, security</li>



<li><strong>Default</strong>: Vận hành</li>



<li><strong>Low</strong>: Marketing, remind, digest</li>
</ul>



<p><strong>Best practice</strong>: Tách queue theo priority, scale worker theo queue. Đặt rule: Job low không bao giờ được làm chậm job critical.</p>



<p><strong>Trade-off</strong>:</p>



<ul class="wp-block-list">
<li>Nhiều queue → Config vận hành phức tạp hơn (supervisor/horizon)</li>



<li>Nhưng nếu không tách, bạn sẽ gặp kiểu &#8220;19h campaign bắn notify&#8221; làm nghẽn payment</li>
</ul>



<p><strong>Lưu ý</strong>: Trong scheduler, bạn có thể chỉ định queue khi schedule job:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#scheduling-queued-jobs">Laravel 12.x Scheduling Queued Jobs</a>, method&nbsp;<code>job()</code>&nbsp;nhận queue name làm tham số thứ 2:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::job(new SendOrderConfirmationJob, 'critical')
    ->dailyAt('09:00');</code></pre>



<p>Hoặc nếu job class đã có property&nbsp;<code>$queue</code>, scheduler sẽ tự động dùng queue đó.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="6-thundering-herd-lúc-tròn-giờ-kẻ-thù-của-db-và-api"><span id="6-thundering-herd-luc-tron-gio-ke-thu-cua-db-va-api">6. &#8220;Thundering herd&#8221; lúc tròn giờ: Kẻ thù của DB và API</span></h2>



<p>Hệ thống lớn rất hay bị &#8220;dồn lịch&#8221; vào 00:00, 09:00, 12:00, 19:00…</p>



<p><strong>Best practice</strong>:</p>



<ul class="wp-block-list">
<li><strong>Stagger</strong>: Dàn đều theo phút/giây</li>



<li>Nếu bắt buộc tròn giờ: Chia nhỏ thành nhiều job chunk, có rate-limit/backoff</li>



<li>Không quét toàn bảng: Dùng cursor/chunk theo index, watermark theo batch</li>
</ul>



<p><strong>Sai lầm thường gặp</strong>: Một job &#8220;tổng hợp cuối ngày&#8221; chạy 00:00 quét cả bảng 50 triệu dòng. Đêm đó DB chết, sáng không ai biết vì không có alert.</p>



<p><strong>Ví dụ — Stagger pattern trong scheduler</strong>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// Dàn đều theo phút để tránh thundering herd
$times = &#91;'9:03', '10:03', '11:03', '12:03', '13:03', '14:03', '15:03', '16:03', '17:03', '18:03', '19:03', '20:03', '21:03'];

foreach ($times as $time) {
    Schedule::job(new ProcessHourlyTaskJob)
        ->at($time);
}</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="7-observability-là-bắt-buộc-không-ai-biết-scheduler-fail--thất-bại-hệ-thống"><span id="7-observability-la-bat-buoc-khong-ai-biet-scheduler-fail-that-bai-he-thong">7. Observability là bắt buộc: &#8220;Không ai biết scheduler fail&#8221; = thất bại hệ thống</span></h2>



<p>Scheduler fail nguy hiểm vì nó&nbsp;<strong>âm thầm</strong>.</p>



<p><strong>Best practice</strong>:</p>



<ul class="wp-block-list">
<li>Log theo batch (scheduledAt, job name, duration, result)</li>



<li>Có alert khi fail (hook/event/ping tùy hệ)</li>



<li>Có &#8220;schedule inventory&#8221;: Định kỳ audit lịch chạy (ít nhất kiểm lịch sau deploy)</li>
</ul>



<p><strong>Trade-off</strong>:</p>



<ul class="wp-block-list">
<li>Thêm log/metrics → Tốn công + tốn storage</li>



<li>Nhưng thiếu nó thì khi sự cố xảy ra, bạn mất nhiều giờ &#8220;đào mộ&#8221;</li>
</ul>



<p><strong>Ví dụ — Scheduler hooks trong Laravel 12</strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#task-hooks">Laravel 12.x Task Hooks</a>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')
    ->daily()
    ->before(function () {
        // The task is about to execute...
    })
    ->after(function () {
        // The task has executed...
    })
    ->onSuccess(function () {
        // The task succeeded...
    })
    ->onFailure(function () {
        // The task failed...
    });</code></pre>



<p><strong>Scheduler setup</strong>:</p>



<pre class="wp-block-code"><code># Cron entry
* * * * * cd /path-to-app &amp;&amp; php artisan schedule:run >> /dev/null 2>&amp;1</code></pre>



<p><strong>Kiểm tra lịch chạy</strong>:</p>



<pre class="wp-block-code"><code># Laravel 12: Xem tất cả scheduled tasks
php artisan schedule:list</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="8-cicd--deploy-đừng-để-deploy-tạo-ra-double-run--half-run"><span id="8-ci-cd-deploy-dung-de-deploy-tao-ra-double-run-half-run">8. CI/CD &amp; Deploy: Đừng để deploy tạo ra double-run / half-run</span></h2>



<p>Ở môi trường deploy liên tục, scheduler/worker cần &#8220;graceful&#8221;:</p>



<p><strong>Best practice</strong>:</p>



<ul class="wp-block-list">
<li>Tránh chạy song song 2 version job logic cho cùng một batch</li>



<li>Worker restart có kiểm soát; job quan trọng có idempotency key để chịu được retry/duplicate</li>



<li>Nếu hệ HA nhiều scheduler node: Phải có chiến lược &#8220;one server&#8221; + shared lock store</li>
</ul>



<p><strong>Sai lầm thường gặp</strong>: Deploy xong &#8220;mọi thứ xanh&#8221;, nhưng lịch 09:00 bị chạy 2 lần vì 2 scheduler node cùng tick.</p>



<p><strong>Ví dụ — Laravel 12&nbsp;<code>onOneServer()</code></strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#running-tasks-on-one-server">Laravel 12.x Running Tasks on One Server</a>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// Nếu có nhiều scheduler server, dùng onOneServer() để chạy chỉ trên 1 server
Schedule::command('emails:send')
    ->daily()
    ->onOneServer(); // Chỉ chạy trên 1 server, dùng cache để lock</code></pre>



<p><strong>Lưu ý</strong>: Job vẫn phải idempotent vì:</p>



<ul class="wp-block-list">
<li>Retry khi fail</li>



<li>Deploy có thể làm job chạy lại</li>



<li>Worker restart có thể làm job chạy lại</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="9-tổ-chức-code-khi-kernelphp-phình-to"><span id="9-to-chuc-code-khi-kernel-php-phinh-to">9. Tổ chức code: Khi Kernel.php &#8220;phình to&#8221;</span></h2>



<p>Khi số lượng lịch tăng,&nbsp;<code>Kernel.php</code>&nbsp;sẽ rất dài (có thể lên 300+ lines).</p>



<p><strong>Best practice — Laravel 12</strong>: Tách helper functions theo domain để dễ maintain:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#defining-schedules">Laravel 12.x Defining Schedules</a>, bạn có thể định nghĩa trong&nbsp;<code>routes/console.php</code>&nbsp;và tách thành helper functions:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// Tách theo domain để dễ maintain
schedulePaymentTasks();
scheduleNotificationTasks();
scheduleCleanupTasks();

function schedulePaymentTasks(): void
{
    Schedule::command('payments:process')
        ->dailyAt('09:00');

    Schedule::command('payments:retry')
        ->dailyAt('14:00');
}

function scheduleNotificationTasks(): void
{
    Schedule::command('notifications:send')
        ->hourly();

    Schedule::command('notifications:digest')
        ->dailyAt('20:00');
}

function scheduleCleanupTasks(): void
{
    Schedule::command('cache:clear')
        ->hourly()
        ->runInBackground();
}</code></pre>



<p>Hoặc dùng&nbsp;<code>withSchedule</code>&nbsp;trong&nbsp;<code>bootstrap/app.php</code>&nbsp;và tách thành methods:</p>



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

// bootstrap/app.php
use Illuminate\Console\Scheduling\Schedule;

->withSchedule(function (Schedule $schedule) {
    schedulePaymentTasks($schedule);
    scheduleNotificationTasks($schedule);
    scheduleCleanupTasks($schedule);
});

function schedulePaymentTasks(Schedule $schedule): void
{
    $schedule->command('payments:process')->dailyAt('09:00');
    $schedule->command('payments:retry')->dailyAt('14:00');
}</code></pre>



<p><strong>Lợi ích</strong>:</p>



<ul class="wp-block-list">
<li>Dễ review: Mỗi function = 1 domain</li>



<li>Dễ test: Có thể test từng domain riêng</li>



<li>Dễ ownership: Team có thể own từng domain</li>



<li>Tránh Kernel.php phình to: Code nằm trong <code>routes/console.php</code> hoặc helper functions</li>
</ul>



<p><strong>Schedule Groups — Laravel 12</strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#schedule-groups">Laravel 12.x Schedule Groups</a>, bạn có thể nhóm các tasks có cùng cấu hình để tránh lặp lại code:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// ❌ SAI: Lặp lại cấu hình cho từng task
Schedule::command('emails:send')
    ->daily()
    ->onOneServer()
    ->timezone('America/New_York');

Schedule::command('emails:prune')
    ->daily()
    ->onOneServer()
    ->timezone('America/New_York');

// ✅ ĐÚNG: Dùng Schedule Groups để nhóm tasks có cùng cấu hình
Schedule::daily()
    ->onOneServer()
    ->timezone('America/New_York')
    ->group(function () {
        Schedule::command('emails:send');
        Schedule::command('emails:prune');
    });</code></pre>



<p><strong>Lợi ích của Schedule Groups</strong>:</p>



<ul class="wp-block-list">
<li><strong>Giảm lặp lại code</strong>: Không cần lặp lại <code>onOneServer()</code>, <code>timezone()</code>, <code>withoutOverlapping()</code> cho từng task</li>



<li><strong>Dễ maintain</strong>: Thay đổi cấu hình một lần áp dụng cho cả nhóm</li>



<li><strong>Tăng tính nhất quán</strong>: Đảm bảo tất cả tasks trong nhóm có cùng cấu hình</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="10-timezone-handling-luôn-nhất-quán"><span id="10-timezone-handling-luon-nhat-quan">10. Timezone handling: Luôn nhất quán</span></h2>



<p><strong>Best practice</strong>: Cố định timezone theo business, không dùng server timezone.&nbsp;<strong>Nguyên tắc</strong>: Tất cả scheduled tasks phải dùng cùng một business timezone để đảm bảo tính nhất quán.</p>



<p><strong>Ví dụ — Laravel 12 timezone</strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#timezones">Laravel 12.x Scheduling Timezones</a>, có 2 cách set timezone:</p>



<p><strong>Cách 1: Set timezone trong config (khuyến nghị)</strong>:</p>



<p>Khi tất cả tasks dùng cùng business timezone, thêm&nbsp;<code>schedule_timezone</code>&nbsp;vào&nbsp;<code>config/app.php</code>:</p>



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

// config/app.php
return &#91;
    'timezone' => 'UTC', // Application timezone

    'schedule_timezone' => 'Asia/Tokyo', // Business timezone cho scheduled tasks
];</code></pre>



<p>Với cấu hình này, tất cả scheduled tasks sẽ tự động dùng timezone&nbsp;<code>Asia/Tokyo</code>&nbsp;trừ khi được ghi đè bởi method&nbsp;<code>timezone()</code>&nbsp;trong từng task cụ thể.</p>



<p><strong>Cách 2: Set timezone cho từng task riêng</strong>:</p>



<p>Chỉ định timezone cho task cụ thể bằng method&nbsp;<code>timezone()</code>:</p>



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

// routes/console.php
use Illuminate\Support\Facades\Schedule;

// Set timezone cho task cụ thể (ghi đè config)
Schedule::command('report:generate')
    ->timezone('America/New_York')
    ->at('2:00');</code></pre>



<p><strong>Lưu ý quan trọng về DST (Daylight Saving Time)</strong>:</p>



<p>Theo&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#timezones">Laravel 12.x Scheduling Timezones</a>, một số timezone sử dụng DST có thể ảnh hưởng đến thời gian thực thi. Khi DST thay đổi, task có thể chạy 2 lần hoặc không chạy.&nbsp;<strong>Nên tránh dùng timezone có DST khi có thể</strong>.</p>



<p><strong>Nguyên tắc áp dụng</strong>:</p>



<ul class="wp-block-list">
<li>✅ <strong>Nên</strong>: Set timezone một lần trong <code>config/app.php</code> với key <code>schedule_timezone</code> nếu tất cả tasks dùng cùng business timezone</li>



<li>⚠️ <strong>Tránh</strong>: Set timezone cho từng task riêng lẻ → dễ quên, dễ sai, khó maintain</li>



<li>⚠️ <strong>Tránh</strong>: Dùng timezone có DST → có thể ảnh hưởng đến thời gian thực thi khi DST thay đổi</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="kết-luận-framework-cho-quyết-định-kỹ-thuật"><span id="ket-luan-framework-cho-quyet-dinh-ky-thuat">Kết luận: Framework cho quyết định kỹ thuật</span></h2>



<p>Khi thêm một scheduled task mới, tôi tự hỏi 6 câu:</p>



<ol class="wp-block-list">
<li><strong>Task này có side-effect quan trọng không?</strong> → Nếu có → Idempotent + audit batch</li>



<li><strong>Dữ liệu tăng thì task tăng theo tuyến nào?</strong> → Nếu O(N) theo bảng lớn → Phải có watermark/chunk/index</li>



<li><strong>Chạy trễ có làm sai cửa sổ dữ liệu không?</strong> → Nếu có → Cần <code>scheduledAt/batchAt</code></li>



<li><strong>Có được phép chạy song song không?</strong> → Nếu không → Overlap guard + lock theo business key</li>



<li><strong>Nó thuộc priority nào?</strong> → Nếu không critical → Đừng để nó chạy chung queue với critical</li>



<li><strong>Fail thì ai biết, trong bao lâu?</strong> → Nếu câu trả lời mơ hồ → Bổ sung observability trước</li>
</ol>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="checklist-áp-dụng"><span id="checklist-ap-dung">Checklist áp dụng</span></h2>



<ul class="wp-block-list">
<li> Scheduler chỉ dispatch queued jobs, không chạy logic nặng</li>



<li> Tất cả job có side-effect đều idempotent</li>



<li> Job dùng <code>batchTime/scheduledAt</code> để tính time window, không dùng <code>now()</code></li>



<li> Job dùng <code>chunkById</code> hoặc cursor pagination cho data lớn</li>



<li> Job có stagger/rate-limit khi gọi external API</li>



<li> Job log theo batch time để dễ đối soát</li>



<li> Scheduler có helper methods theo domain, không phình to <code>schedule()</code></li>



<li> Timezone nhất quán: Business timezone, không dùng server timezone</li>



<li> Queue được phân priority (critical/default/low)</li>



<li> Có alert khi scheduler/job fail</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p><strong>Tài liệu tham khảo</strong>:&nbsp;<a href="https://laravel.com/docs/12.x/scheduling#main-content">Laravel 12.x Task Scheduling</a></p>
<p>The post <a href="https://blog.tomosia.com.vn/laravel-scheduler-tu-duy-thiet-ke-trade-off-va-best-practices-cho-he-thong-lon/">Laravel Scheduler: Tư duy thiết kế, trade-off và best practices cho hệ thống lớn</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/laravel-scheduler-tu-duy-thiet-ke-trade-off-va-best-practices-cho-he-thong-lon/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Hướng dẫn kết nối Laravel + MongoDB</title>
		<link>https://blog.tomosia.com.vn/huong-dan-ket-noi-laravel-mongodb/</link>
					<comments>https://blog.tomosia.com.vn/huong-dan-ket-noi-laravel-mongodb/#comments</comments>
		
		<dc:creator><![CDATA[Ninh nguyen]]></dc:creator>
		<pubDate>Fri, 12 Jan 2024 06:12:18 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=3065</guid>

					<description><![CDATA[<p>1. Khái niệm MongoDB MongoDB là một database hướng tài liệu (document), một dạng NoSQL database. Vì thế, MongoDB&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-ket-noi-laravel-mongodb/">Hướng dẫn kết nối Laravel + MongoDB</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="1-khai-niem-mongodb" class="wp-block-heading"><strong>1. Khái niệm MongoDB</strong></h2>



<p><strong>MongoDB</strong> là một database hướng tài liệu (document), một dạng NoSQL database. Vì thế, MongoDB sẽ tránh cấu trúc table-based của relational database để thích ứng với các tài liệu như JSON có một schema rất linh hoạt gọi là BSON. Sử dụng lưu trữ dữ liệu dưới dạng Document JSON nên mỗi một collection sẽ các các kích cỡ và các document khác nhau. Các dữ liệu được lưu trữ trong document kiểu JSON nên truy vấn sẽ rất nhanh.</p>



<h2 id="2-khi-nao-su-dung-mongodb" class="wp-block-heading"><strong>2. Khi nào sử dụng MongoDB</strong></h2>



<p><strong>Quản lý và truyền tải content</strong>&nbsp;– Quản lý đa dạng nhiều product của content chỉ trong một kho lưu trữ data cho phép thay đổi và phản hồi nhanh chóng mà không chịu thêm phức tạp thêm từ hệ thống content.</p>



<p><strong>Cấu trúc Mobile và Social</strong>&nbsp;– MongoDB cung cấp một platform có sẵn, phản xạ nhanh, và dễ mở rộng cho phép rất nhiều khả năng đột phá, phân tích real-time, và hỗ trợ toàn cầu.</p>



<p><strong>Quản lý data khách hàng</strong>&nbsp;– Tận dụng khả năng query nhanh chóng cho phân tích real-time trên cơ sở dữ liệu người dùng cực lớn vớ các mô hình data phức tạp bằng các schema linh hoạt và tự động sharding cho mở rộng chiều ngang.</p>



<h2 id="3-uu-diem-va-nhuoc-diem" class="wp-block-heading"><strong>3. Ưu điểm và nhược điểm</strong></h2>



<h2 id="uu-diem" class="wp-block-heading"><strong>Ưu điểm</strong></h2>



<p>Dữ liệu lưu trữ phi cấu trúc, không có tính ràng buộc, toàn vẹn nên tính sẵn sàng cao, hiệu suất lớn và dễ dàng mở rộng lưu trữ.</p>



<p>Dữ liệu được caching (ghi đệm) lên RAM, hạn chế truy cập vào ổ cứng nên tốc độ đọc và ghi cao.</p>



<h2 id="nhuoc-diem" class="wp-block-heading"><strong>Nhược điểm</strong></h2>



<p>Không ứng dụng được cho các mô hình giao dịch nào có yêu cầu độ chính xác cao do không có ràng buộc.</p>



<p>Không có cơ chế transaction (giao dịch) để phục vụ các ứng dụng ngân hàng.</p>



<p>Dữ liệu lấy RAM làm trọng tâm hoạt động vì vậy khi hoạt động yêu cầu một bộ nhớ RAM lớn.</p>



<p>Mọi thay đổi về dữ liệu mặc định đều chưa được ghi xuống ổ cứng ngay lập tức vì vậy khả năng bị mất dữ liệu từ nguyên nhân mất điện đột xuất là rất cao.</p>



<h2 id="4-ket-noi-laravel-mongodb" class="wp-block-heading"><strong>4. Kết nối Laravel + MongoDB</strong></h2>



<h2 id="tao-du-an-laravel" class="wp-block-heading">Tạo dự án laravel</h2>



<p>composer create-project laravel/laravel Demo</p>



<h2 id="them-package-mongodb-vao-du-an" class="wp-block-heading">Thêm package mongoDB vào dự án</h2>



<p>composer require mongodb/laravel-mongodb:4.0.0</p>



<h2 id="them-cau-hinh-mongodb" class="wp-block-heading">Thêm cấu hình mongoDB</h2>



<p>Thêm vào file config/database.php</p>



<pre class="wp-block-code"><code>'connections' =&gt; &#91;
        'mongodb' =&gt; &#91;
            'driver' =&gt; 'mongodb',
            'dsn' =&gt; env('MONGODB_URI'),
            'database' =&gt; 'YOUR_DATABASE_NAME',
        ],</code></pre>



<p>Để lấy các thông tin cấu hình mongoDB truy cập <a href="https://cloud.mongodb.com/v2/659bc617e6426b75883e79c7#/overview">connection mongoDB</a> </p>



<p>Tạo Database</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="348" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-1024x348.png" alt="" class="wp-image-3071" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-1024x348.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-300x102.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-768x261.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-380x129.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53-800x272.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-20-53.png 1044w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Chọn connect-&gt;Drivers</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="828" height="449" src="http://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34.png" alt="" class="wp-image-3072" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34.png 828w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34-300x163.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34-768x416.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34-380x206.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2024/01/Screenshot-from-2024-01-11-22-29-34-800x434.png 800w" sizes="auto, (max-width: 828px) 100vw, 828px" /></figure>



<p>Khi đó bạn sẽ lấy được chuỗi kết nối có dạng như thế này</p>



<pre class="wp-block-code"><code>mongodb+srv://&lt;username&gt;:&lt;password&gt;@test.hlpfzgm.mongodb.net/?retryWrites=true&amp;w=majority</code></pre>



<p>Cập nhật vào file .env</p>



<p>MONGODB_URI=mongodb+srv://&lt;username&gt;:&lt;password&gt;@test.hlpfzgm.mongodb.net/?retryWrites=true&amp;w=majority</p>



<p>Lưu ý: &lt;username&gt; và &lt;password&gt; là username và password đã được nhập ở bước tạo database</p>



<p>Cập nhật vào file config/database.php</p>



<pre class="wp-block-code"><code>'default' =&gt; env('DB_CONNECTION', 'mongodb'),</code></pre>



<h2 id="kiem-tra-ket-noi" class="wp-block-heading">Kiểm tra kết nối</h2>



<pre class="wp-block-code"><code>Route::get('/ping', function (Request  $request) {    
&nbsp;&nbsp;&nbsp;&nbsp;$connection = DB::connection('mongodb');
&nbsp;&nbsp;&nbsp;&nbsp;$msg = 'Thành công!';
&nbsp;&nbsp;&nbsp;&nbsp;try {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$connection-&gt;command(&#91;'ping' =&gt; 1]);  
    } catch (\Exception  $e) {  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$msg = 'Thất bại: ' . $e-&gt;getMessage();
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return &#91;'msg' =&gt; $msg];
});</code></pre>



<h2 id="tao-migration-va-model" class="wp-block-heading">Tạo migration và model</h2>



<pre class="wp-block-code"><code>public function up(): void
    {
        Schema::create('customers', function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;string('name');
            $table-&gt;string('email');
            $table-&gt;string('age');
            $table-&gt;timestamps();
        });
    }</code></pre>



<pre class="wp-block-code"><code>Chạy lệnh:
php artisan migration
php artisan make:model Customer</code></pre>



<pre class="wp-block-code"><code>//use Illuminate\Database\Eloquent\Model;
 use MongoDB\Laravel\Eloquent\Model;

class  Customer  extends  Model
{
&nbsp;&nbsp;&nbsp;&nbsp;use  HasFactory;

    protected  $connection = 'mongodb';

&nbsp;&nbsp;&nbsp;&nbsp;protected  $collection = 'customers';

&nbsp;&nbsp;&nbsp;&nbsp;protected  $fillable = &#91;'name','email','age'];
}</code></pre>



<h2 id="crud" class="wp-block-heading">CRUD</h2>



<pre class="wp-block-code"><code>//Thêm mới
CustomerSQL::create(&#91;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'name'=&gt; 'customer_1',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'email'=&gt; 'customer1@gmail.com',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'age' =&gt; '18',
]);</code></pre>



<pre class="wp-block-code"><code>//Lấy thông tin customers
Customer::where('name', 'customer_1')-&gt;get();</code></pre>



<p>Hết rồi, hy vọng bài viết có thể giúp ích được cho bạn<br>Cảm ơn và hẹn gặp lại =)) !!!</p>
<p>The post <a href="https://blog.tomosia.com.vn/huong-dan-ket-noi-laravel-mongodb/">Hướng dẫn kết nối Laravel + MongoDB</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/huong-dan-ket-noi-laravel-mongodb/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Tản mạn về Object Relational Mapping</title>
		<link>https://blog.tomosia.com.vn/tan-man-ve-object-relational-mapping/</link>
					<comments>https://blog.tomosia.com.vn/tan-man-ve-object-relational-mapping/#comments</comments>
		
		<dc:creator><![CDATA[Thien Tran]]></dc:creator>
		<pubDate>Tue, 19 Dec 2023 06:22:34 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2238</guid>

					<description><![CDATA[<p>ORM là gì ? ORM là 1 kỹ thuật lập trình giúp ánh xạ các record dữ liệu&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/tan-man-ve-object-relational-mapping/">Tản mạn về Object Relational Mapping</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<h2 id="orm-la-gi" class="wp-block-heading">ORM là gì ?</h2>



<p>ORM là 1 <strong><em>kỹ thuật lập trình</em></strong> giúp ánh xạ các <strong>record</strong> dữ liệu trong hệ quản trị cơ sở dữ liệu sang dạng đối tượng đang định nghĩa trong các class &#8211; một khái niệm phổ biến được sử dụng trong tất cả các ngôn ngữ hiện đại ngày nay như: Java, PHP, Ruby. Bạn có thể áp dụng kĩ thật này với bất cứ dự án nào bạn thích</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="700" height="359" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm.webp" alt="" class="wp-image-2245" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm.webp 700w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm-300x154.webp 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm-380x195.webp 380w" sizes="auto, (max-width: 700px) 100vw, 700px" /></figure>



<p>Có hai patterns được áp dụng cho các cách thiết kế ORM khác nhau.</p>



<ul class="wp-block-list">
<li>Active Record: Một số ORM nổi tiếng sử dụng như: <strong><em>Eloquent, CachePHP, JOOQ, TOPLINK</em></strong></li>



<li>Data Mapper: Một số ORM nổi tiếng sử dụng như: <strong><em>Doctrine, Hibernate, SqlAlchemy</em></strong></li>
</ul>



<h2 id="orm-hoat-dong-nhu-the-nao" class="wp-block-heading">ORM hoạt động như thế nào ?</h2>



<p>Cơ bản thì ORM gói gọn CSDL trong 1 object, 1 phần sẽ chứa data phần còn lại sẽ xử lý để biến nó thành CSDL quan hệ.</p>



<p>ORM biểu thị các đối tượng ở đó dữ liệu được hiển thị dưới dạng Object</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="735" height="530" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm1.webp" alt="" class="wp-image-2248" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm1.webp 735w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm1-300x216.webp 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/orm1-380x274.webp 380w" sizes="auto, (max-width: 735px) 100vw, 735px" /></figure>
</div></div>



<h2 id="uu-nhuoc-diem-cua-orm" class="wp-block-heading">Ưu nhược điểm của ORM</h2>



<p>Ví dụ về lấy danh sách người dùng cùng với bài đăng mới nhất của mỗi người dùng:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.854169845581055px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$usersWithLatestPosts = DB::select('
    SELECT u.*, p.*
    FROM users u
    LEFT JOIN (
        SELECT user_id, MAX(created_at) AS latest_post_date
        FROM posts
        GROUP BY user_id
    ) latest_posts ON u.id = latest_posts.user_id
    LEFT JOIN posts p ON u.id = p.user_id AND latest_posts.latest_post_date = p.created_at
');" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$usersWithLatestPosts </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DB</span><span style="color: #F286C4">::</span><span style="color: #62E884">select</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #E7EE98">    SELECT u.*, p.*</span></span>
<span class="line"><span style="color: #E7EE98">    FROM users u</span></span>
<span class="line"><span style="color: #E7EE98">    LEFT JOIN (</span></span>
<span class="line"><span style="color: #E7EE98">        SELECT user_id, MAX(created_at) AS latest_post_date</span></span>
<span class="line"><span style="color: #E7EE98">        FROM posts</span></span>
<span class="line"><span style="color: #E7EE98">        GROUP BY user_id</span></span>
<span class="line"><span style="color: #E7EE98">    ) latest_posts ON u.id = latest_posts.user_id</span></span>
<span class="line"><span style="color: #E7EE98">    LEFT JOIN posts p ON u.id = p.user_id AND latest_posts.latest_post_date = p.created_at</span></span>
<span class="line"><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">);</span></span></code></pre></div>



<p>Đây là cách sử dụng ORM trong laravel</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.425346374511719px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$usersWithLatestPosts = User::with(['posts' =&gt; function ($query) {
    $query-&gt;latest()-&gt;limit(1);
}])-&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$usersWithLatestPosts </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">User</span><span style="color: #F286C4">::</span><span style="color: #62E884">with</span><span style="color: #F6F6F4">([</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">posts</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($query) {</span></span>
<span class="line"><span style="color: #F6F6F4">    $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">latest</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">limit</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">}])</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Như ví dụ ở trên chúng ta sẽ thấy rõ được 1 số ưu điểm khi sử dụng ORM đó là đoạn code sẽ ngắn gọn hơn, đọc vào dễ hiểu và thân thiện với coder hơn. Đó cũng là một ưu điểm nổi bật của ORM khi được áp dụng vào các dự án.</p>



<p>Ưu điểm:</p>



<ul class="wp-block-list">
<li><strong>Dễ sử dụng và đọc</strong></li>



<li><strong>Tính di động và linh hoạt</strong></li>



<li><strong>Bảo mật dữ liệu</strong></li>



<li><strong>Tương thích với quan hệ đối tượng</strong></li>



<li><strong>Tiết kiệm thời gian phát triển</strong></li>
</ul>



<p>Nhược điểm:</p>



<ul class="wp-block-list">
<li><strong>Hiệu suất không cao nhất</strong></li>



<li><strong>Tùy chỉnh phức tạp</strong></li>



<li><strong>Phụ thuộc vào ORM</strong></li>



<li><strong>Tối ưu hóa không linh hoạt</strong></li>
</ul>



<h2 id="conclusion" class="wp-block-heading">Conclusion</h2>



<p>Nên mix giữa ORM và Raw SQL sao cho hiệu quả. Với các truy vấn thông thường thì ORM là lựa chọn của mình. Với các tình huống phải thao tác với nhiều bản ghi, raw SQL có vẻ hợp lí hơn. Nhưng khi dùng raw sql bạn phải thực sự cẩn thận về vấn đề Sql Injection.</p>



<p>Cảm ơn các bạn đã theo dõi bài viết trên, trong bài viết có tham khảo từ một số nguồn khác. 🙏🙏🙏</p>
<p>The post <a href="https://blog.tomosia.com.vn/tan-man-ve-object-relational-mapping/">Tản mạn về Object Relational Mapping</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/tan-man-ve-object-relational-mapping/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title>Xây dựng phương thức truy vấn tuỳ chỉnh với Laravel Eloquent Builder Macro</title>
		<link>https://blog.tomosia.com.vn/xay-dung-phuong-thuc-truy-van-tuy-chinh-voi-laravel-eloquent-builder-macro/</link>
					<comments>https://blog.tomosia.com.vn/xay-dung-phuong-thuc-truy-van-tuy-chinh-voi-laravel-eloquent-builder-macro/#comments</comments>
		
		<dc:creator><![CDATA[Cong Thinh Dang]]></dc:creator>
		<pubDate>Mon, 18 Dec 2023 08:44:12 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2659</guid>

					<description><![CDATA[<p>Khi bạn đối mặt với việc lặp lại nhiều câu truy vấn giống nhau. Laravel Eloquent Builder Macro&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/xay-dung-phuong-thuc-truy-van-tuy-chinh-voi-laravel-eloquent-builder-macro/">Xây dựng phương thức truy vấn tuỳ chỉnh với Laravel Eloquent Builder Macro</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Khi bạn đối mặt với việc lặp lại nhiều câu truy vấn giống nhau. <strong>Laravel Eloquent Builder Macro</strong> sẽ là công cụ cứu cánh cho sự trừu tượng hóa, giúp mã nguồn của bạn trở nên linh hoạt hơn và giảm bớt gánh nặng của sự phức tạp.<br><br><strong>Ví dụ một chút nhé:</strong><br>Bạn đang muốn tìm kiếm các ứng viên có xuất hiện từ <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">{$key}</mark> trong tên hoặc địa chỉ. Đoạn code của bạn trông sẽ như thế này:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;where('name', 'like', &quot;%{$key}%&quot;)
        -&gt;orWhere('address', 'like', &quot;%{$key}%&quot;)
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">orWhere</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Và mỗi lần thêm trường tìm kiếm, bạn sẽ phải viết thêm vài dòng where như thế này nữa, và còn nhiều nơi khác trong source code đang làm điều tương tự, quá nhiều where &#8220;like&#8221; đúng không nào.</p>



<p><strong>Sử dụng Laravel Eloquent Builder Macro</strong></p>



<p>Bạn chỉ cần vào <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">App\Providers\AppServiceProvider</mark> và khai báo đoạn code sau vào phương thức boot().</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="public function boot()
{
  Builder::macro('whereLike', function ($column, $value = null, $boolean = 'and') {
      return $this-&gt;where($column, 'like', $value, $boolean);
  });
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">boot</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1; font-style: italic">Builder</span><span style="color: #F286C4">::</span><span style="color: #62E884">macro</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">whereLike</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($column, $value </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, $boolean </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">and</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">($column, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, $value, $boolean);</span></span>
<span class="line"><span style="color: #F6F6F4">  });</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>Hoặc bạn cũng có thể đăng ký một <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-luminous-vivid-orange-color">Service Providers</mark> như sau:</p>



<p>1. Sử dụng Artisan Command:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="php artisan make:provider MarcoServiceProvider" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #BF9EEE">php</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">artisan</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">make</span><span style="color: #F6F6F4">:</span><span style="color: #BF9EEE">provider</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">MarcoServiceProvider</span></span></code></pre></div>



<p>Lệnh này sẽ tạo ra một file mới tên là MarcoServiceProvider.php trong thư mục app/Providers.</p>



<p>2. Khởi tạo chức năng với phương thức boot():</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="public function boot()
{
  Builder::macro('whereLike', function ($column, $value = null, $boolean = 'and') {
      return $this-&gt;where($column, 'like', $value, $boolean);
  });
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">boot</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #97E1F1; font-style: italic">Builder</span><span style="color: #F286C4">::</span><span style="color: #62E884">macro</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">whereLike</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($column, $value </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, $boolean </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">and</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">      </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">($column, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, $value, $boolean);</span></span>
<span class="line"><span style="color: #F6F6F4">  });</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>3. Đăng ký Service Provider:<br>Mở file config/app.php và thêm Service Provider vào mảng providers.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="'providers' =&gt; [
    // ...
    App\Providers\MarcoServiceProvider::class,
]," style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">providers</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// ...</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1; font-style: italic">App</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Providers</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">MarcoServiceProvider</span><span style="color: #F286C4">::class</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">],</span></span></code></pre></div>



<p>Bây giờ bạn đã có thể dùng câu truy vấn whereLike rồi (happy).</p>



<p>Đoạn code ví dụ sẽ được viết lại như thế này:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;whereLike('name', &quot;%{$key}%&quot;)
        -&gt;whereLike('address', &quot;%{$key}%&quot;, 'or')
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Làm một cái gì đó gọn hơn nhé.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="// App\Providers\MacroServiceProvider
Builder::macro('whereLike', function ($columns = [], $value = null, $boolean = 'and') {
    return array_map(function ($column) use ($value, $boolean) {
        $this-&gt;where($column, 'like', $value, $boolean);
    }, $columns);
});" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">// App\Providers\MacroServiceProvider</span></span>
<span class="line"><span style="color: #97E1F1; font-style: italic">Builder</span><span style="color: #F286C4">::</span><span style="color: #62E884">macro</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">whereLike</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($columns </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [], $value </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, $boolean </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">and</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">array_map</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">function</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">($column)</span><span style="color: #62E884"> </span><span style="color: #F286C4">use</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">($value,</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$boolean)</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #62E884">        </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">($column,</span><span style="color: #62E884"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$value,</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$boolean);</span></span>
<span class="line"><span style="color: #62E884">    </span><span style="color: #F6F6F4">},</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$columns);</span></span>
<span class="line"><span style="color: #F6F6F4">});</span></span></code></pre></div>



<p>Và khi sử dụng thì bạn chỉ cần truyền một array vào whereLike là đã ok rồi.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;whereLike(['name', 'address'], &quot;%{$key}%&quot;, 'or')
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">([</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">], </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Nhưng nếu một lúc nào đó, bạn chỉ muốn tìm kiếm những ứng viên có đang hoạt động (status = 1) thôi thì whereLike này có thể sẽ không còn đúng nữa. Ví dụ:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;whereLike(['name', 'address'], &quot;%{$key}%&quot;, 'or')
        -&gt;where('status', 1)
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">([</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">], </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">status</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Đoạn code được viết lại:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;where('name', 'like', &quot;%{$key}%&quot;)
        -&gt;orWhere('address', 'like', &quot;%{$key}%&quot;)
        -&gt;where('status', 1)
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">orWhere</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">status</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Vậy bây giờ, việc cần làm là định nghĩa thêm một truy vấn mới và tái sử dụng whereLike.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Builder::macro('subWhereLike', function ($columns = [], $value = null, $boolean = 'or') {
    return $this-&gt;where(function ($q) use ($columns, $value, $boolean) {
        $q-&gt;whereLike($columns, $value, $boolean);
    });
});" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Builder</span><span style="color: #F286C4">::</span><span style="color: #62E884">macro</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">subWhereLike</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($columns </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [], $value </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, $boolean </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($q) </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> ($columns, $value, $boolean) {</span></span>
<span class="line"><span style="color: #F6F6F4">        $q</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">($columns, $value, $boolean);</span></span>
<span class="line"><span style="color: #F6F6F4">    });</span></span>
<span class="line"><span style="color: #F6F6F4">});</span></span></code></pre></div>



<p>Đã ổn hơn rồi, câu truy vấn đã được viết lại.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;where(function ($q) use ($key) {
            $q-&gt;where('name', 'like', &quot;%{$key}%&quot;)
            $q-&gt;orWhere('address', 'like', &quot;%{$key}%&quot;)
        })
        -&gt;where('status', 1)
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($q) </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> ($key) {</span></span>
<span class="line"><span style="color: #F6F6F4">            $q</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">            $q</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">orWhere</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">address</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        })</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">status</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Trong một vài trường hợp, bạn mong muốn thêm một câu truy vấn khác nữa thì việc truyền thêm một Closure là điều cần thiết. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Builder::macro('subWhereLike', function ($columns = [], $value = null, $boolean = 'or', Closure $closure = null) {
    $query = $this-&gt;where(function ($q) use ($columns, $value, $boolean, $closure) {
        $q-&gt;whereLike($columns, $value, $boolean);
        
        if ($closure !== null) {
            $closure($q);
        }
    });

    return $query;
});" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Builder</span><span style="color: #F286C4">::</span><span style="color: #62E884">macro</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">subWhereLike</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($columns </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [], $value </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">, $boolean </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #97E1F1; font-style: italic">Closure</span><span style="color: #F6F6F4"> $closure </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">    $query </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($q) </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> ($columns, $value, $boolean, $closure) {</span></span>
<span class="line"><span style="color: #F6F6F4">        $q</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereLike</span><span style="color: #F6F6F4">($columns, $value, $boolean);</span></span>
<span class="line"><span style="color: #F6F6F4">        </span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> ($closure </span><span style="color: #F286C4">!==</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">null</span><span style="color: #F6F6F4">) {</span></span>
<span class="line"><span style="color: #F6F6F4">            $closure($q);</span></span>
<span class="line"><span style="color: #F6F6F4">        }</span></span>
<span class="line"><span style="color: #F6F6F4">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> $query;</span></span>
<span class="line"><span style="color: #F6F6F4">});</span></span></code></pre></div>



<p>Sử dụng thôi nào!</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="Candidate::query()
        -&gt;subWhereLike(['email', 'lastname'], &quot;%{$key}%&quot;, 'or', 
            function ($query) use ($key) {
                $query-&gt;orWhere('phone', $key);
        })
        -&gt;where('status', 1)
        -&gt;get();" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1; font-style: italic">Candidate</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">subWhereLike</span><span style="color: #F6F6F4">([</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">email</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">lastname</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">], </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%{</span><span style="color: #F6F6F4">$key</span><span style="color: #E7EE98">}%</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">or</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> ($query) </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> ($key) {</span></span>
<span class="line"><span style="color: #F6F6F4">                $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">orWhere</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">phone</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, $key);</span></span>
<span class="line"><span style="color: #F6F6F4">        })</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">status</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span></code></pre></div>



<p>Hết rồi, bạn hãy thực hành và cảm nhận nhé!!!</p>
<p>The post <a href="https://blog.tomosia.com.vn/xay-dung-phuong-thuc-truy-van-tuy-chinh-voi-laravel-eloquent-builder-macro/">Xây dựng phương thức truy vấn tuỳ chỉnh với Laravel Eloquent Builder Macro</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/xay-dung-phuong-thuc-truy-van-tuy-chinh-voi-laravel-eloquent-builder-macro/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Có gì mới ở PHP 8.3?</title>
		<link>https://blog.tomosia.com.vn/co-gi-moi-o-php-8-3/</link>
					<comments>https://blog.tomosia.com.vn/co-gi-moi-o-php-8-3/#comments</comments>
		
		<dc:creator><![CDATA[admin_tomosia]]></dc:creator>
		<pubDate>Fri, 15 Dec 2023 09:39:19 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2592</guid>

					<description><![CDATA[<p>PHP 8.3 đã được released vào ngày 23/11/2023 và có nhiều tính năng cũng như cải tiến mới&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/co-gi-moi-o-php-8-3/">Có gì mới ở PHP 8.3?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>PHP 8.3 đã được released vào ngày 23/11/2023 và có nhiều tính năng cũng như cải tiến mới kể từ khi PHP 8.2 ra mắt. Mặc dù nó chính thức được coi là một bản released nhỏ nhưng một số thay đổi trong 8.3 có thể ảnh hưởng trực tiếp đến công việc của chúng ta với PHP, có thể giúp chúng ta viết mã nhanh hơn và ít lỗi hơn.</p>



<p>Hãy cùng tìm hiểu PHP có những tính năng nào mới và sự thay đổi nào ở bản phát hành mới nhất này.</p>



<h2 id="1-typed-class-constants" class="wp-block-heading">1. Typed Class Constants</h2>



<p>PHP 8.3 trở lên hỗ trợ khai báo kiểu dữ liệu cho các hằng số (constant) class PHP. Điều này đảm bảo tính tương thích về kiểu của các constant khi các class con và các class implement interface ghi đè lên chúng.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="// Khai báo kiểu dữ liệu sau từ khoá &quot;const&quot;
interface I {
    const string PHP = 'PHP 8.3';
}

class X {
  protected const string FOO = 'foo';
}

trait Y {
  public const string BAR = 'bar';
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #7B7F8B">// Khai báo kiểu dữ liệu sau từ khoá &quot;const&quot;</span></span>
<span class="line"><span style="color: #F286C4">interface</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">I</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">PHP 8.3</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">X</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">protected</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">foo</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">trait</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Y</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">BAR</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bar</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Điều gì xảy ra nếu giá trị của constant có type khác với type đã khai báo:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="class X {
    const string PHP = 1;
}

// Fatal error: Cannot use int as value for class constant X::PHP of type string" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">X</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">1</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">// Fatal error: Cannot use int as value for class constant X::PHP of type string</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Ở PHP version trước 8.3, chúng ta có thể override lại giá trị của constant khi triển khai interface hoặc class cha:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="interface I {
    const PHP = 'PHP 8.2';
}

class Foo implements I {
    const PHP = [];
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">interface</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">I</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">PHP 8.2</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Foo</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">I</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [];</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Nhưng PHP 8.3 đã ngăn chặn việc thay đổi type của constant không tương thích với type đã khai báo ở interface hoặc class cha:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="interface I {
    const string PHP = 'PHP 8.3';
}

class Foo implements I {

    // Illegal
    // Fatal error: Cannot use array as value for class constant Foo::PHP       
    // of type string
    const string PHP = [];
    
    // Illegal:
    // Type must be declared if it was specified in the base class
    const PHP = &quot;PHP 8.3&quot;;

    // Illegal:
    // In this case, we can't change the type declared in the 
    // base class, even if the new type and its value are compatible.
    const float PHP = 8.3;
    
    // Legal:
    // It's OK
    const string PHP = &quot;PHP 8.3.x.y.z&quot;;
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">interface</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">I</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">PHP 8.3</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Foo</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">implements</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">I</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Illegal</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Fatal error: Cannot use array as value for class constant Foo::PHP       </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// of type string</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [];</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Illegal:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Type must be declared if it was specified in the base class</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">PHP 8.3</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Illegal:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// In this case, we can&#39;t change the type declared in the </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// base class, even if the new type and its value are compatible.</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">float</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">8.3</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Legal:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// It&#39;s OK</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">PHP</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">PHP 8.3.x.y.z</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<ul class="wp-block-list">
<li>Chúng ta có thể khai báo nhiều kiểu theo dạng &#8220;narrowed&#8221;:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="class Foo {
    public const string|int FOO = 'foo';
}

class Bar extends Foo {
    public const string FOO = 'bar';
    public const int FOO = 8;
    
    // Illegal:
    // Không thể mở rộng thêm types ở class con
    public const string|int|float FOO = 8;
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Foo</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F286C4">|</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">foo</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Bar</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Foo</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">bar</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">int</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">8</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Illegal:</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Không thể mở rộng thêm types ở class con</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">string</span><span style="color: #F286C4">|</span><span style="color: #97E1F1; font-style: italic">int</span><span style="color: #F286C4">|</span><span style="color: #97E1F1; font-style: italic">float</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">8</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>❗️ Hai types <strong><em>void</em></strong> và <em><strong>never</strong></em> không được hỗ trợ khi khai báo constant.</p>



<h2 id="2-dynamic-class-constant-fetch" class="wp-block-heading">2. <strong>Dynamic class constant fetch</strong></h2>



<ul class="wp-block-list">
<li>Việc fetch các constants, có thể sử dụng hàm <code>constant()</code>. Ở PHP version trước 8.3, chúng ta sẽ làm thế này:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="class Foo {
    public const THE_FOO = 9;
}

enum FooEnum: int {
    case FirstMember = 9;
    case SecondMember = 10;
}

$constantName = 'THE_FOO';
$enumName = 'FirstMember';

echo constant('Foo::' . $constantName); // 9
echo constant('FooEnum::' . $enumName)-&gt;value; // 9" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Foo</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">const</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">THE_FOO</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">9</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">enum</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FooEnum</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">int</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">FirstMember</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">9</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">SecondMember</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">10</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$constantName </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">THE_FOO</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">$enumName </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">FirstMember</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">constant</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Foo::</span><span style="color: #DEE492">&#39;</span><span style="color: #62E884"> </span><span style="color: #F286C4">.</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$constantName); </span><span style="color: #7B7F8B">// 9</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">constant</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">FooEnum::</span><span style="color: #DEE492">&#39;</span><span style="color: #62E884"> </span><span style="color: #F286C4">.</span><span style="color: #62E884"> </span><span style="color: #F6F6F4">$enumName)</span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4">value; </span><span style="color: #7B7F8B">// 9</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Từ PHP 8.3 trở đi, chúng ta có thể fetch chúng một cách linh động và gọn gàng hơn:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="echo Foo::{$constantName};
echo FooEnum::{$enumName}-&gt;value;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Foo</span><span style="color: #F286C4">::</span><span style="color: #F6F6F4">{$constantName};</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">FooEnum</span><span style="color: #F286C4">::</span><span style="color: #F6F6F4">{$enumName}</span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4">value;</span></span></code></pre></div>



<h2 id="3-json_validate-function" class="wp-block-heading">3. <code>json_validate</code>()&nbsp;function</h2>



<p>Khi làm việc với dữ liệu JSON-encoded, chúng ta muốn biết liệu phần payload có hợp lệ về mặt cú pháp hay không trước khi làm điều gì đó với nó.</p>



<ul class="wp-block-list">
<li>Trước PHP 8.3, cách duy nhất để xác định xem một chuỗi có phải là chuỗi JSON hợp lệ hay không là cố gắng decode nó và xem liệu có lỗi nào không:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="function json_check(string $string): bool {
    json_decode($string);

    return json_last_error() === JSON_ERROR_NONE;
}

$string = '{ &quot;test&quot;: { &quot;foo&quot;: &quot;bar&quot; } '
echo json_check($string); // false" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">json_check</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">string</span><span style="color: #F6F6F4"> $string)</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">bool</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E1F1">json_decode</span><span style="color: #F6F6F4">($string);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">json_last_error</span><span style="color: #F6F6F4">() </span><span style="color: #F286C4">===</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">JSON_ERROR_NONE</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$string </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">{ &quot;test&quot;: { &quot;foo&quot;: &quot;bar&quot; } </span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">json_check</span><span style="color: #F6F6F4">($string); </span><span style="color: #7B7F8B">// false</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Ở PHP 8.3 này,  một hàm mới được bổ sung có tên <code>json_validate()</code> trả về <code>true</code> hoặc <code>false</code> để kiểm tra chuỗi đã cho có phải là chuỗi JSON hợp lệ hay không:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$string = '{ &quot;test&quot;: { &quot;foo&quot;: &quot;bar&quot; } '
echo json_validate($string); // false" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$string </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">{ &quot;test&quot;: { &quot;foo&quot;: &quot;bar&quot; } </span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">json_validate</span><span style="color: #F6F6F4">($string); </span><span style="color: #7B7F8B">// false</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Nếu <code>json_validate() </code>trả về <code>false</code>, nguyên nhân có thể được truy xuất bằng cách sử dụng <code>json_last_error()</code> và <code>json_last_error_msg()</code>.</li>
</ul>



<p>❗️<strong>Important: </strong>Nếu bạn không làm gì với những thuộc tính trong payload của chuỗi JSON và chỉ cần kiểm tra JSON có hợp lệ hay không để làm 1 việc gì đó (insert vào DB chẳng hạn), thì sử dụng <code>json_validate()</code> sẽ sử dụng ít bộ nhớ hơn <code>json_decode()</code>, vì nó không cần phải mã hoá hay xây dựng cấu trúc array/object trong chuỗi JSON trên.</p>



<h2 id="4-override-attribute" class="wp-block-heading">4. <code>#[\Override]</code><strong>&nbsp;attribute</strong></h2>



<p>Giả sử chúng ta muốn override method <code>test()</code> của lớp cha, nhưng lại gõ sai thành <code>best()</code>, việc này ngầm hiểu là tạo ra method mới có tên là <code>best()</code>, không đúng mục đích ban đầu là override lại method <code>test()</code>.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="class Parent {
  protected function test(): void {}
}

class Child extends Parent {
  public function best(): void {}
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Parent</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">protected</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">test</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span><span style="color: #F6F6F4"> {}</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Child</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Parent</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">best</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span><span style="color: #F6F6F4"> {}</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>PHP 8.3 cung cấp một thuộc tính mới có tên<code> #[\Override]</code> có thể được thêm vào các method class. </p>



<ul class="wp-block-list">
<li>Khi thuộc tính <code>#[\Override]</code> được thêm vào một method class có extend class cha hoặc implement một interface, PHP bắt buộc rằng method đó phải override hoặc triển khai method của class cha hoặc interface.</li>



<li>Mục đích là để ngăn chặn việc chúng ta muốn override/triển khai method của class cha nhưng lại vô tình viết sai tên method hoặc method đó đã bị xoá khỏi lớp cha.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="class Parent {
  protected function test(): void {}
}

class Child extends Parent {
  #[\Override]
  public function test(): void {}
  // Good
}

class Grand extends Parent {
  #[\Override]
  public function best(): void {}
  // Bad:
  // Fatal error: Grand::best() has #[\Override] attribute,
  // but no matching parent method exists
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Parent</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">protected</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">test</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span><span style="color: #F6F6F4"> {}</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Child</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Parent</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  #[</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Override</span><span style="color: #F6F6F4">]</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">test</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span><span style="color: #F6F6F4"> {}</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B">// Good</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Grand</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Parent</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">  #[</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Override</span><span style="color: #F6F6F4">]</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">best</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span><span style="color: #F6F6F4"> {}</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B">// Bad:</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B">// Fatal error: Grand::best() has #[\Override] attribute,</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #7B7F8B">// but no matching parent method exists</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<h2 id="5-deep-cloning-of-readonly-properties" class="wp-block-heading">5. Deep Cloning of&nbsp;<code>readonly</code>&nbsp;Properties</h2>



<p>Trong PHP 8.1, các thuộc tính <code>readonly</code> đã được giới thiệu, tiếp theo là class <code>readonly</code> trong PHP 8.2. </p>



<p>Một vấn đề khác là khi clone các đối tượng. Việc clone các đối tượng và thuộc tính <code>readonly</code> là điều không thể.</p>



<p>Kể từ PHP 8.3, bạn có thể linh hoạt khởi tạo lại hoặc unset các thuộc tính <code>readonly</code> trong quá trình clone.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="readonly class Foo
{
    public function __construct(private DateTime $createdAt)
    {
    }

    public function getCreatedAt(): DateTime
    {
        return $this-&gt;createdAt;
    }

    public function __clone(): void
    {
        $this-&gt;createdAt = clone $this-&gt;createdAt;
    }
}

$instance = new Foo(new DateTime('2023-12-15'));
$cloned = clone $instance;

// For PHP &lt; 8.3:
// PHP Fatal error: Uncaught Error: Cannot modify readonly 
// property Foo::$createdAt in Foo.php

// For PHP 8.3:
$cloned = clone $instance;
$cloned-&gt;getCreatedAt()-&gt;setDate(2023, 12, 16);" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">readonly</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Foo</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">__construct</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">private</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DateTime</span><span style="color: #F6F6F4"> $createdAt)</span></span>
<span class="line"><span style="color: #F6F6F4">    {</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">getCreatedAt</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DateTime</span></span>
<span class="line"><span style="color: #F6F6F4">    {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4">createdAt;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">__clone</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">void</span></span>
<span class="line"><span style="color: #F6F6F4">    {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4">createdAt </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">clone</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #F6F6F4">createdAt;</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$instance </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Foo</span><span style="color: #F6F6F4">(</span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">DateTime</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">2023-12-15</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">));</span></span>
<span class="line"><span style="color: #F6F6F4">$cloned </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">clone</span><span style="color: #F6F6F4"> $instance;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">// For PHP &lt; 8.3:</span></span>
<span class="line"><span style="color: #7B7F8B">// PHP Fatal error: Uncaught Error: Cannot modify readonly </span></span>
<span class="line"><span style="color: #7B7F8B">// property Foo::$createdAt in Foo.php</span></span>
<span class="line"></span>
<span class="line"><span style="color: #7B7F8B">// For PHP 8.3:</span></span>
<span class="line"><span style="color: #F6F6F4">$cloned </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">clone</span><span style="color: #F6F6F4"> $instance;</span></span>
<span class="line"><span style="color: #F6F6F4">$cloned</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getCreatedAt</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">setDate</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">2023</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">12</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">16</span><span style="color: #F6F6F4">);</span></span></code></pre></div>



<h2 id="6-randomizergetbytesfromstring-method" class="wp-block-heading">6.<strong>&nbsp;</strong><code>Randomizer::getBytesFromString()</code><strong>&nbsp;method</strong></h2>



<p>PHP 8.3 hỗ trợ phương thức <code>Randomizer::getBytesFromString()</code> mới trả về một chuỗi số ngẫu nhiên từ chuỗi đầu vào và có độ dài được yêu cầu.</p>



<p>Cơ hội để một byte được chọn tỷ lệ thuận với tỷ lệ xuất hiện của nó trong chuỗi đầu vào. Nếu mỗi byte xuất hiện với số lần như nhau thì mỗi byte đều có khả năng được chọn như nhau.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$random = new Random\Randomizer();
$alpha = 'ABCDEFGHJKMNPQRSTVWXYZ';

$random-&gt;getBytesFromString($alpha, 6); //  &quot;MBXGWL&quot;
$random-&gt;getBytesFromString($alpha, 6); //  &quot;LESPMG&quot;

$randomDomain = sprintf(
    &quot;%s.example.com&quot;,
    $randomizer-&gt;getBytesFromString(
        'abcdefghijklmnopqrstuvwxyz0123456789',
        16,
    ),
);

echo $randomDomain;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$random </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Random</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Randomizer</span><span style="color: #F6F6F4">();</span></span>
<span class="line"><span style="color: #F6F6F4">$alpha </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">ABCDEFGHJKMNPQRSTVWXYZ</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$random</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getBytesFromString</span><span style="color: #F6F6F4">($alpha, </span><span style="color: #BF9EEE">6</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">//  &quot;MBXGWL&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">$random</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getBytesFromString</span><span style="color: #F6F6F4">($alpha, </span><span style="color: #BF9EEE">6</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">//  &quot;LESPMG&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$randomDomain </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">sprintf</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #62E884">    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">%s.example.com</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #62E884">    </span><span style="color: #F6F6F4">$randomizer</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getBytesFromString</span><span style="color: #F6F6F4">(</span></span>
<span class="line"><span style="color: #62E884">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">abcdefghijklmnopqrstuvwxyz0123456789</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #62E884">        </span><span style="color: #BF9EEE">16</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #62E884">    </span><span style="color: #F6F6F4">),</span></span>
<span class="line"><span style="color: #F6F6F4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> $randomDomain;</span></span></code></pre></div>



<p>Lưu ý rằng phương thức <code>Randomizer::getBytesFromString()</code> hoạt động ở cấp độ byte. Nó không thể xáo trộn một cách hiệu quả các ký tự nhiều byte như biểu tượng cảm xúc, ký tự CJK.</p>



<h2 id="7-randomizergetfloat-and-randomizernextfloat-method" class="wp-block-heading">7.<strong>&nbsp;</strong><code>Randomizer::<code>getFloat</code>()</code> and <code>Randomizer::<code>nextFloat</code>()</code> <strong>method</strong></h2>



<p>PHP 8.3 thêm các phương thức <code>Randomizer::getFloat()</code> và <code>Randomizer::nextFloat()</code> tạo ra giá trị float ngẫu nhiên.</p>



<h3 id="7-1-randomizergetfloat" class="wp-block-heading">7.1. <code>Randomizer::getFloat()</code></h3>



<ul class="wp-block-list">
<li>Ví dụ, generate 1 số float có khoảng giá trị 0 -&gt; 5:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$random = new Random\Randomizer();

$random-&gt;getFloat(0, 5); // 2.3937446906217" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$random </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Random</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Randomizer</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$random</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getFloat</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">5</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">// 2.3937446906217</span></span></code></pre></div>



<p>Phương thức <code>Randomizer::getFloat()</code> chấp nhận <code>\Random\IntervalBoundary</code> Enum làm tham số thứ ba để cho biết liệu các giá trị <code>min</code> và <code>max</code> có phải bao gồm ở kết quả trả về hay không.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="enum IntervalBoundary {
    case ClosedOpen;
    case ClosedClosed;
    case OpenClosed;
    case OpenOpen;
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">enum</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">IntervalBoundary</span><span style="color: #F6F6F4"> {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ClosedOpen</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">ClosedClosed</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">OpenClosed</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">case</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">OpenOpen</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>Rules như sau:</p>



<ul class="wp-block-list">
<li><code>IntervalBoundary::ClosedOpen</code>: <em>$min</em> có thể được return, <em>$max</em> có thể <strong>không</strong>.</li>



<li><code>IntervalBoundary::ClosedClosed</code>: cả <em>$min</em> và <em>$max</em> đều có thể được return.</li>



<li><code>IntervalBoundary::OpenClosed</code>: <em>$min</em> có thể <strong>không được </strong>return, <em>$max</em> có thể.</li>



<li><code>IntervalBoundary::OpenOpen</code>: Cả <em>$min</em> và <em>$max</em> đều <strong>không được</strong> return.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$random = new Random\Randomizer();

$random-&gt;getFloat(0, 10, \Random\IntervalBoundary::OpenOpen); 
// 9.3835746900717" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$random </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Random</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Randomizer</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$random</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">getFloat</span><span style="color: #F6F6F4">(</span><span style="color: #BF9EEE">0</span><span style="color: #F6F6F4">, </span><span style="color: #BF9EEE">10</span><span style="color: #F6F6F4">, </span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Random</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">IntervalBoundary</span><span style="color: #F286C4">::</span><span style="color: #BF9EEE">OpenOpen</span><span style="color: #F6F6F4">); </span></span>
<span class="line"><span style="color: #7B7F8B">// 9.3835746900717</span></span></code></pre></div>



<h3 id="7-2-randomizernextfloat" class="wp-block-heading">7.2. <code>Randomizer::nextFloat()</code></h3>



<p>Method <code>Randomizer::nextFloat()</code> mới có chức năng giống hệt với <code>Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen</code>), trả về một giá trị ngẫu nhiên trong phạm vi <code>0 &lt;= x &lt; 1</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$random = new Random\Randomizer();

$random-&gt;nextFloat(); // 0.3767414902847" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$random </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">new</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Random</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Randomizer</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">$random</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">nextFloat</span><span style="color: #F6F6F4">(); </span><span style="color: #7B7F8B">// 0.3767414902847</span></span></code></pre></div>



<h2 id="8-php-cli-lint-php-l-supports-linting-multiple-files-at-once" class="wp-block-heading">8. PHP CLI Lint (<code>php -l</code>) supports linting multiple files at once</h2>



<p>PHP CLI cung cấp tính năng Linting để kiểm tra tên tệp đã truyền để tìm lỗi syntax. Điều này rất hữu ích để kiểm tra nhanh tệp PHP hoặc đoạn mã trước khi thực thi.</p>



<ul class="wp-block-list">
<li>Ở PHP &lt; 8.3, chỉ thực hiện kiểm tra lỗi syntax ở file đầu tiên được khai báo:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Zsh</span><span role="button" tabindex="0" data-code="php -l foo.php bar.php
No syntax errors detected in foo.php" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">php</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-l</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">foo.php</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">bar.php</span></span>
<span class="line"><span style="color: #62E884">No</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">syntax</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">errors</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">detected</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">in</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">foo.php</span></span></code></pre></div>



<ul class="wp-block-list">
<li>Ở PHP 8.3:</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Zsh</span><span role="button" tabindex="0" data-code="php -l foo.php bar.php
No syntax errors detected in foo.php
No syntax errors detected in bar.php" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #62E884">php</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">-l</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">foo.php</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">bar.php</span></span>
<span class="line"><span style="color: #62E884">No</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">syntax</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">errors</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">detected</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">in</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">foo.php</span></span>
<span class="line"><span style="color: #62E884">No</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">syntax</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">errors</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">detected</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">in</span><span style="color: #F6F6F4"> </span><span style="color: #E7EE98">bar.php</span></span></code></pre></div>



<h2 id="9-mb_str_pad-function" class="wp-block-heading">9. <code>mb_str_pad()</code> function </h2>



<p>PHP cung cấp hàm <code>str_pad</code> được sử dụng để đệm một chuỗi đến một độ dài xác định bằng một chuỗi khác.</p>



<p>Tuy nhiên, hàm <code>str_pad</code> không hỗ trợ các ký tự nhiều byte, dẫn đến các vấn đề khi xử lý các ngôn ngữ sử dụng mã hóa nhiều byte như UTF-8.</p>



<p>Kể từ PHP 8.3, hàm <code>mb_str_pad</code> được cung cấp để giải quyết hạn chế trước đó của hàm <code>str_pad</code> trong việc xử lý mã hóa ký tự nhiều byte như UTF-8.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="echo mb_str_pad('卞卞', 6, '义', STR_PAD_BOTH); 
// 义义卞卞义义

echo mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_RIGHT); 
 // string(18) &quot;▶▶❤❓❇❤&quot;
 
echo mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_LEFT);  
// string(18) &quot;❤❓❇❤▶▶&quot;" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">mb_str_pad</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">卞卞</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">6</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">义</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">STR_PAD_BOTH</span><span style="color: #F6F6F4">); </span></span>
<span class="line"><span style="color: #7B7F8B">// 义义卞卞义义</span></span>
<span class="line"></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">mb_str_pad</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">▶▶</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">6</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">❤❓❇</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">STR_PAD_RIGHT</span><span style="color: #F6F6F4">); </span></span>
<span class="line"><span style="color: #F6F6F4"> </span><span style="color: #7B7F8B">// string(18) &quot;▶▶❤❓❇❤&quot;</span></span>
<span class="line"><span style="color: #F6F6F4"> </span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">mb_str_pad</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">▶▶</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">6</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">❤❓❇</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #BF9EEE">STR_PAD_LEFT</span><span style="color: #F6F6F4">);  </span></span>
<span class="line"><span style="color: #7B7F8B">// string(18) &quot;❤❓❇❤▶▶&quot;</span></span></code></pre></div>



<h2 id="10-str_increment-str_decrement-functions" class="wp-block-heading">10. str_increment(), str_decrement() functions</h2>



<p>Kể từ PHP 8.3, các hàm <code>str_increment</code> có thể được sử dụng tăng một chuỗi chữ và số, và hàm <code>str_decrement</code> giảm một chuỗi chữ và số. </p>



<p>Các hàm này có thể đặc biệt hữu ích trong các tình huống mà bạn cần quản lý các chuỗi chữ và số đại diện cho các giá trị tuần tự.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="echo str_increment('ABC'); // ABD
echo str_decrement('ABC'); // ABB
echo str_increment('39'); // 40
echo str_decrement('38'); // 37" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">str_increment</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">ABC</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">// ABD</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">str_decrement</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">ABC</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">// ABB</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">str_increment</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">39</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">// 40</span></span>
<span class="line"><span style="color: #97E1F1">echo</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">str_decrement</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">38</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">); </span><span style="color: #7B7F8B">// 37</span></span></code></pre></div>



<p>Vẫn còn một vài tính năng mới và thay đổi trong version này, mọi người có thể xem thêm tại: <a href="https://www.php.net/releases/8.3/en.php#other_new_things" target="_blank" rel="noreferrer noopener">https://www.php.net/releases/8.3/en.php#other_new_things</a><br><br>Cảm ơn mọi người đã dành thời gian để đọc 🙇‍♂️<br><br></p>
<p>The post <a href="https://blog.tomosia.com.vn/co-gi-moi-o-php-8-3/">Có gì mới ở PHP 8.3?</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/co-gi-moi-o-php-8-3/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>Progressive Web Applications (PWAs) in Laravel Applications</title>
		<link>https://blog.tomosia.com.vn/progressive-web-applications-pwas-in-laravel-applications/</link>
					<comments>https://blog.tomosia.com.vn/progressive-web-applications-pwas-in-laravel-applications/#comments</comments>
		
		<dc:creator><![CDATA[Duy Nguyễn Hoàng]]></dc:creator>
		<pubDate>Wed, 13 Dec 2023 01:20:07 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2550</guid>

					<description><![CDATA[<p>Giới thiệu về PWA Ứng dụng Progressive Web Apps (PWA) với Laravel kết hợp những lợi ích của&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/progressive-web-applications-pwas-in-laravel-applications/">Progressive Web Applications (PWAs) in Laravel Applications</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h4 class="wp-block-heading" id="4838"><span id="gioi-thieu-ve-pwa"><strong>Giới thiệu về PWA</strong></span></h4>



<p id="9c69">Ứng dụng Progressive Web Apps (PWA) với Laravel kết hợp những lợi ích của cả ứng dụng web và di động, mang đến trải nghiệm nhanh, khả năng hoạt động offline và cập nhật tự động cho người dùng. Những ứng dụng này cung cấp trải nghiệm giống như ứng dụng di động trực tiếp trong trình duyệt web, cho phép người dùng truy cập ứng dụng mượt mà mà không phụ thuộc vào điều kiện mạng hay thiết bị của họ. Với Progressive Web Apps, người dùng có thể trải nghiệm những lợi ích của một ứng dụng di động mà không cần phải tải về và cài đặt riêng biệt.</p>



<h4 class="wp-block-heading" id="f43a"><span id="nhung-loi-ich-cua-pwas">Những lợi ích của PWAs</span></h4>



<p>Progressive Web Apps (PWAs) đại diện cho một kiểu mới của ứng dụng web kết hợp những tính năng tốt nhất của ứng dụng web truyền thống và ứng dụng di động native. Bằng cách tận dụng các công nghệ web hiện đại, PWAs mang lại trải nghiệm giống như ứng dụng. Một số ưu điểm chính của PWAs so với ứng dụng web thông thường bao gồm:</p>



<p id="2f69">1.&nbsp;<strong>Offline Access:</strong>&nbsp;PWAs có thể lưu trữ dữ liệu cục bộ, cho phép người dùng truy cập nội dung ứng dụng ngay cả khi họ không có kết nối internet. Tính năng này làm cho PWAs trở nên đáng tin cậy và thuận tiện hơn cho người dùng, vì họ không còn phải lo lắng về vấn đề kết nối.</p>



<p id="e9e7">2.&nbsp;<strong>Push Notifications:</strong>&nbsp;PWAs có thể gửi thông báo đến người dùng, giữ cho họ được thông tin và tương tác với ứng dụng. Tính năng này giúp tăng cường sự tương tác và giữ chân người dùng, vì họ có khả năng quay lại ứng dụng nếu họ nhận được thông báo đều đặn.</p>



<p id="c0c5">3.&nbsp;<strong>App-Like Interface:</strong>&nbsp;PWAs cung cấp giao diện người dùng giống như ứng dụng, nâng cao trải nghiệm tổng thể. Giao diện này trực quan và dễ sử dụng, khiến cho nó trở nên hấp dẫn hơn đối với người dùng quen thuộc với việc sử dụng ứng dụng di động native.</p>



<p id="577d">4.&nbsp;<strong>Fast Loading Times:</strong>&nbsp;PWAs được tối ưu hóa cho tốc độ, đảm bảo một trải nghiệm người dùng mượt mà. Tính năng này đặc biệt quan trọng trong thế giới kỹ thuật số ngày nay, nơi người dùng mong đợi ứng dụng tải nhanh và hiệu quả.</p>



<h4 id="trien-khai-pwa-trong-ung-dung-laravel" class="wp-block-heading"><strong>Triển khai PWA trong ứng dụng Laravel</strong></h4>



<p><br><br>1.  Cài đặt thư viện:<br><br>Mở file composer.json và thêm đoạn mã sau</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">JSON</span><span role="button" tabindex="0" data-code="&quot;require&quot;: {
    &quot;silviolleite/laravelpwa&quot;: &quot;~2.0.3&quot;,
}," style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">require</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">: {</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #97E2F2">&quot;</span><span style="color: #97E1F1">silviolleite/laravelpwa</span><span style="color: #97E2F2">&quot;</span><span style="color: #F286C4">:</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">~2.0.3</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">},</span></span></code></pre></div>



<p>Hoặc bạn có thể mở terminal của project lên chạy lệnh<br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="composer require silviolleite/laravelpwa --prefer-dist
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #BF9EEE">composer</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">require</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">silviolleite</span><span style="color: #F286C4">/</span><span style="color: #BF9EEE">laravelpwa</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">--</span><span style="color: #BF9EEE">prefer</span><span style="color: #F286C4">-</span><span style="color: #BF9EEE">dist</span></span>
<span class="line"></span></code></pre></div>



<p>Để thêm file config thì sẽ chạy lệnh</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="$ php artisan vendor:publish --provider=&quot;LaravelPWA\Providers\LaravelPWAServiceProvider&quot;
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">$ </span><span style="color: #BF9EEE">php</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">artisan</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE">vendor</span><span style="color: #F6F6F4">:</span><span style="color: #BF9EEE">publish</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">--</span><span style="color: #BF9EEE">provider</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">LaravelPWA\Providers\LaravelPWAServiceProvider</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"></span></code></pre></div>



<p>Một file <code>config/laravelpwa.php</code> sẽ được sinh ra, bạn có thể thay đổi thông tin tên ứng dụng, màu sắc, kích thược ở đây để phù hợp với ứng dụng của mình.<br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="'manifest' =&gt; [
        'name' =&gt; env('APP_NAME', 'My PWA App'),
        'short_name' =&gt; 'PWA',
        'start_url' =&gt; '/',
        'background_color' =&gt; '#ffffff',
        'theme_color' =&gt; '#000000',
        'display' =&gt; 'standalone',
        'orientation' =&gt; 'any',
        'status_bar' =&gt; 'black',
        'icons' =&gt; [
            '72x72' =&gt; [
                'path' =&gt; '/images/icons/icon-72x72.png',
                'purpose' =&gt; 'any'
            ],
            '96x96' =&gt; [
                'path' =&gt; '/images/icons/icon-96x96.png',
                'purpose' =&gt; 'any'
            ],
            '128x128' =&gt; [
                'path' =&gt; '/images/icons/icon-128x128.png',
                'purpose' =&gt; 'any'
            ],
            '144x144' =&gt; [
                'path' =&gt; '/images/icons/icon-144x144.png',
                'purpose' =&gt; 'any'
            ],
            '152x152' =&gt; [
                'path' =&gt; '/images/icons/icon-152x152.png',
                'purpose' =&gt; 'any'
            ],
            '192x192' =&gt; [
                'path' =&gt; '/images/icons/icon-192x192.png',
                'purpose' =&gt; 'any'
            ],
            '384x384' =&gt; [
                'path' =&gt; '/images/icons/icon-384x384.png',
                'purpose' =&gt; 'any'
            ],
            '512x512' =&gt; [
                'path' =&gt; '/images/icons/icon-512x512.png',
                'purpose' =&gt; 'any'
            ],
        ],
        'splash' =&gt; [
            '640x1136' =&gt; '/images/icons/splash-640x1136.png',
            '750x1334' =&gt; '/images/icons/splash-750x1334.png',
            '828x1792' =&gt; '/images/icons/splash-828x1792.png',
            '1125x2436' =&gt; '/images/icons/splash-1125x2436.png',
            '1242x2208' =&gt; '/images/icons/splash-1242x2208.png',
            '1242x2688' =&gt; '/images/icons/splash-1242x2688.png',
            '1536x2048' =&gt; '/images/icons/splash-1536x2048.png',
            '1668x2224' =&gt; '/images/icons/splash-1668x2224.png',
            '1668x2388' =&gt; '/images/icons/splash-1668x2388.png',
            '2048x2732' =&gt; '/images/icons/splash-2048x2732.png',
        ],
        'shortcuts' =&gt; [
            [
                'name' =&gt; 'Shortcut Link 1',
                'description' =&gt; 'Shortcut Link 1 Description',
                'url' =&gt; '/shortcutlink1',
                'icons' =&gt; [
                    &quot;src&quot; =&gt; &quot;/images/icons/icon-72x72.png&quot;,
                    &quot;purpose&quot; =&gt; &quot;any&quot;
                ]
            ],
            [
                'name' =&gt; 'Shortcut Link 2',
                'description' =&gt; 'Shortcut Link 2 Description',
                'url' =&gt; '/shortcutlink2'
            ]
        ],
        'custom' =&gt; []
    ]" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">manifest</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">env</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">APP_NAME</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span><span style="color: #62E884"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">My PWA App</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">),</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">short_name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">PWA</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">start_url</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">background_color</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">#ffffff</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">theme_color</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">#000000</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">display</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">standalone</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">orientation</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">status_bar</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">black</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">icons</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">72x72</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-72x72.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">96x96</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-96x96.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">128x128</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-128x128.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">144x144</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-144x144.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">152x152</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-152x152.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">192x192</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-192x192.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">384x384</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-384x384.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">512x512</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-512x512.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">        ],</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">splash</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">640x1136</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-640x1136.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">750x1334</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-750x1334.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">828x1792</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-828x1792.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1125x2436</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1125x2436.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1242x2208</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1242x2208.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1242x2688</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1242x2688.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1536x2048</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1536x2048.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1668x2224</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1668x2224.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">1668x2388</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-1668x2388.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">            </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">2048x2732</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/splash-2048x2732.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        ],</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">shortcuts</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">            [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Shortcut Link 1</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">description</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Shortcut Link 1 Description</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">url</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/shortcutlink1</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">icons</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">src</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">/images/icons/icon-72x72.png</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                    </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&quot;</span></span>
<span class="line"><span style="color: #F6F6F4">                ]</span></span>
<span class="line"><span style="color: #F6F6F4">            ],</span></span>
<span class="line"><span style="color: #F6F6F4">            [</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">name</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Shortcut Link 2</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">description</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">Shortcut Link 2 Description</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">                </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">url</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/shortcutlink2</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">            ]</span></span>
<span class="line"><span style="color: #F6F6F4">        ],</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">custom</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> []</span></span>
<span class="line"><span style="color: #F6F6F4">    ]</span></span></code></pre></div>



<p>2. Bạn cũng có thể chỉ định kích thước của hình ảnh<br><br></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="[
    'path' =&gt; '/images/icons/icon-512x512.png',
    'sizes' =&gt; '512x512',
    'purpose' =&gt; 'any'
]," style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">[</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">path</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">/images/icons/icon-512x512.png</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">sizes</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">512x512</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">purpose</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">=&gt;</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">any</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">],</span></span></code></pre></div>



<p>3. Thêm vào blade của dự án</p>



<p>Trong view layout bạn chỉ cần thêm <code>@laravelPWA</code> và trong thẻ <code>&lt;head&gt;</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Blade</span><span role="button" tabindex="0" data-code="<html&gt;

<head&gt;
  <title&gt;App Name - @yield('title')</title&gt;
  <meta charset=&quot;utf-8&quot;&gt;
  <meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  @vite('resources/css/app.css')
  @vite('resources/js/app.js')
  @laravelPWA
</head&gt;

<body class=&quot;bg-gradient-to-r from-emerald-400 to-cyan-400&quot;&gt;
  @section('header')
    @include('layouts.partials.header')
  @show
  @section('sidebar')
  @show
  @yield('content')
</body&gt;

</html&gt;
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F6F6F4">&lt;</span><span style="color: #F286C4">html</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">&lt;</span><span style="color: #F286C4">head</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">  &lt;</span><span style="color: #F286C4">title</span><span style="color: #F6F6F4">&gt;App Name - </span><span style="color: #F286C4">@yield</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">title</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)&lt;/</span><span style="color: #F286C4">title</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">  &lt;</span><span style="color: #F286C4">meta</span><span style="color: #F6F6F4"> </span><span style="color: #62E884; font-style: italic">charset</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">utf-8</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">  &lt;</span><span style="color: #F286C4">meta</span><span style="color: #F6F6F4"> </span><span style="color: #62E884; font-style: italic">name</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">viewport</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4"> </span><span style="color: #62E884; font-style: italic">content</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">width=device-width, initial-scale=1.0</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">@vite</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">resources/css/app.css</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">@vite</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">resources/js/app.js</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #62E884">@laravelPWA</span></span>
<span class="line"><span style="color: #F6F6F4">&lt;/</span><span style="color: #F286C4">head</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">&lt;</span><span style="color: #F286C4">body</span><span style="color: #F6F6F4"> </span><span style="color: #62E884; font-style: italic">class</span><span style="color: #F286C4">=</span><span style="color: #DEE492">&quot;</span><span style="color: #E7EE98">bg-gradient-to-r from-emerald-400 to-cyan-400</span><span style="color: #DEE492">&quot;</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">@section</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">header</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">@include</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">layouts.partials.header</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">@show</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">@section</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">sidebar</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">@show</span></span>
<span class="line"><span style="color: #F6F6F4">  </span><span style="color: #F286C4">@yield</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"><span style="color: #F6F6F4">&lt;/</span><span style="color: #F286C4">body</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">&lt;/</span><span style="color: #F286C4">html</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"></span></code></pre></div>



<p>Chế độ xem ngoại tuyến<br><br>Theo mặc định, chế độ xem ngoại tuyến sẽ được triển khai trong thư mục <code>resources/views/vendor/laravelpwa/offline.blade.php</code>. Bạn có thể custom để phù hợp với ứng dụng của mình ở đây<br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="772" height="194" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-2.17.03-PM.png" alt="" class="wp-image-2555" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-2.17.03-PM.png 772w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-2.17.03-PM-300x75.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-2.17.03-PM-768x193.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-2.17.03-PM-380x95.png 380w" sizes="auto, (max-width: 772px) 100vw, 772px" /></figure>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">Blade</span><span role="button" tabindex="0" data-code="@extends('layouts.app')

@section('content')

    <h1&gt;You are currently not connected to any networks.</h1&gt;

@endsection" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">@extends</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">layouts.app</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">@section</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    &lt;</span><span style="color: #F286C4">h1</span><span style="color: #F6F6F4">&gt;You are currently not connected to any networks.&lt;/</span><span style="color: #F286C4">h1</span><span style="color: #F6F6F4">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">@endsection</span></span></code></pre></div>



<p>4. Kiểm tra ứng dụng<br><br>Để kiểm thử PWA của bạn, mở ứng dụng Laravel trong trình duyệt (Chrome, Firefox). Nhấp vào nút &#8220;Install&#8221; xuất hiện trong thanh địa chỉ của trình duyệt để cài đặt PWA lên màn hình chính của bạn.<br>Đây là example của mình:<br></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="660" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-1024x660.png" alt="" class="wp-image-2556" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-1024x660.png 1024w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-300x193.png 300w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-768x495.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-1536x990.png 1536w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-2048x1320.png 2048w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-380x245.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-800x516.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM-1160x748.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.32.57-PM.png 2638w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="917" height="1024" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-917x1024.png" alt="" class="wp-image-2557" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-917x1024.png 917w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-269x300.png 269w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-768x858.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-380x424.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-800x893.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM-1160x1295.png 1160w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-12-at-1.19.39-PM.png 1372w" sizes="auto, (max-width: 917px) 100vw, 917px" /></figure>



<h4 id="tong-ket" class="wp-block-heading"><strong>Tổng kết</strong></h4>



<p>PWA mang lại nhiều lợi ích, bao gồm hiệu suất, trải nghiệm người dùng, offline&#8230; . Nó có thể là một xu hướng mới trong phát triển web tương lại.<br>Đây là nhưng kiến thức mình tham khảo được từ nhiều nguồn. Hy vọng mọi người cảm thấy thú vị và có thể áp dụng vào dự án. Cảm ơn mọi người đã dành thời gian đọc ạ ☺️☺️☺️</p>



<p><em>Nguồn: </em></p>



<p>Thư viện : <a href="https://github.com/silviolleite/laravel-pwa">https://github.com/silviolleite/laravel-pwa</a><br>Bài viết tham khảo: <a href="https://medium.com/@technophille/a-guide-to-implementing-pwa-in-laravel-applications-dc5e8e460197">https://medium.com/@technophille/a-guide-to-implementing-pwa-in-laravel-applications-dc5e8e460197</a></p>
<p>The post <a href="https://blog.tomosia.com.vn/progressive-web-applications-pwas-in-laravel-applications/">Progressive Web Applications (PWAs) in Laravel Applications</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/progressive-web-applications-pwas-in-laravel-applications/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Sort và filter dữ liệu dễ dàng hơn với Laravel Purity</title>
		<link>https://blog.tomosia.com.vn/sort-va-filter-du-lieu-de-dang-hon-voi-laravel-purity/</link>
					<comments>https://blog.tomosia.com.vn/sort-va-filter-du-lieu-de-dang-hon-voi-laravel-purity/#comments</comments>
		
		<dc:creator><![CDATA[Thien Tran]]></dc:creator>
		<pubDate>Sun, 10 Dec 2023 14:59:39 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://blog.tomosia.com.vn/?p=2484</guid>

					<description><![CDATA[<p>Giới thiệu về Laravel Purity Laravel purity là một thư viện hỗ trợ filter và sort data một&#8230;</p>
<p>The post <a href="https://blog.tomosia.com.vn/sort-va-filter-du-lieu-de-dang-hon-voi-laravel-purity/">Sort và filter dữ liệu dễ dàng hơn với Laravel Purity</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="gioi-thieu-ve-laravel-purity" class="wp-block-heading">Giới thiệu về Laravel Purity</h2>



<p>Laravel purity là một thư viện hỗ trợ filter và sort data một cách dễ dàng đơn giản hỗ trợ trực tiếp cho laravel framework. Để biết được sự lợi hại của thư viện này. Hãy cùng mình mò mẫm nó nhé. Let&#8217;s gooooooo !!!</p>



<h2 id="dat-van-de" class="wp-block-heading">Đặt vấn đề</h2>



<p>Khi chúng ta viết api để filter nhiều giá trị cùng một lúc thì service của chúng ta sẽ khá dài dòng và rườm rà. Để viết ra một hàm filter dùng chung thì sẽ tốn khá nhiều thời gian và chất xám. Vì vậy thư viện này sinh ra với mục đích tối giản hơn về cách filter và sort dữ liệu.</p>



<h2 id="loi-ich-khi-su-dung" class="wp-block-heading">Lợi ích khi sử dụng</h2>



<p>Khi chúng ta viết một cách bình thường:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="public function index(Request $request)
{
    $query = Post::query();

    // Lọc theo tiêu đề
    if ($request-&gt;has('title')) {
        $query-&gt;where('title', 'like', '%' . $request-&gt;input('title') . '%');
    }

    // Lọc theo nội dung (content)
    if ($request-&gt;has('content')) {
        $query-&gt;where('content', 'like', '%' . $request-&gt;input('content') . '%');
    }

    // Lọc theo ngày xuất bản (published_at)
    if ($request-&gt;has('published_at')) {
        $query-&gt;whereDate('published_at', '=', $request-&gt;input('published_at'));
    }

    $posts = $query-&gt;get();

    return response()-&gt;json($posts);
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">index</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">Request</span><span style="color: #F6F6F4"> $request)</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">    $query </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F286C4">::</span><span style="color: #62E884">query</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Lọc theo tiêu đề</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> ($request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">has</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">title</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)) {</span></span>
<span class="line"><span style="color: #F6F6F4">        $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">title</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">%</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">.</span><span style="color: #F6F6F4"> $request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">input</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">title</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">.</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">%</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Lọc theo nội dung (content)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> ($request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">has</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)) {</span></span>
<span class="line"><span style="color: #F6F6F4">        $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">where</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">like</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">%</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">.</span><span style="color: #F6F6F4"> $request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">input</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">) </span><span style="color: #F286C4">.</span><span style="color: #F6F6F4"> </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">%</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #7B7F8B">// Lọc theo ngày xuất bản (published_at)</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">if</span><span style="color: #F6F6F4"> ($request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">has</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">published_at</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">)) {</span></span>
<span class="line"><span style="color: #F6F6F4">        $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">whereDate</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">published_at</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">=</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">, $request</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">input</span><span style="color: #F6F6F4">(</span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">published_at</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">));</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    $posts </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> $query</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">response</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">json</span><span style="color: #F6F6F4">($posts);</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p>Khi dùng thư viện trên </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:8.4296875px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="public function index(Request $request)
{
    $query = Post::filter()-&gt;get();

    return response()-&gt;json($posts);
}" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">index</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">Request</span><span style="color: #F6F6F4"> $request)</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">    $query </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Post</span><span style="color: #F286C4">::</span><span style="color: #62E884">filter</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">get</span><span style="color: #F6F6F4">();</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">response</span><span style="color: #F6F6F4">()</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">json</span><span style="color: #F6F6F4">($posts);</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span></code></pre></div>



<p><strong><em>Thật là ngắn gọn và súc tích phải không nào </em></strong>!!!</p>



<h2 id="cach-cai-dat-va-su-dung" class="wp-block-heading">Cách cài đặt và sử dụng</h2>



<p>Mở terminal của project lên và cài bằng composer nhé: </p>



<pre class="wp-block-code"><code>composer require abbasudo/laravel-purity </code></pre>



<pre class="wp-block-code"><code>php artisan vendor:publish --tag=purity </code></pre>



<p>Lệnh này để publish thư mục `config/purity` cho phép chúng ta có thể custom được filter mà chúng ta muốn.</p>



<p>Vậy là thư viện đã sẵn sàng để sử dụng. Tiếp theo hãy tìm tới Model mà bạn muốn filter nhé. Ở đây mình đang filter cho Model Post</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#f6f6f4;--cbp-line-number-width:16.859375px;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#333545;color:#ebebe6">PHP</span><span role="button" tabindex="0" data-code="&lt;?php

namespace App\Models;

use Abbasudo\Purity\Traits\Filterable;
use Abbasudo\Purity\Traits\Sortable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;
    use Filterable;
    use Sortable;

    protected $filterFields = [
        'id',
        'title',
        'content',
        'published_at',
        'category_id',
        'category'
    ];

    public function category()
    {
        return $this-&gt;belongsTo(Category::class);
    }
}
" style="color:#f6f6f4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dracula-soft" style="background-color: #282A36" tabindex="0"><code><span class="line"><span style="color: #F286C4">&lt;?</span><span style="color: #BF9EEE">php</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">namespace</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">App</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Models</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Abbasudo</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Purity</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Traits</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Filterable</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Abbasudo</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Purity</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Traits</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Sortable</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Illuminate</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Database</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Eloquent</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Factories</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">HasFactory</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Illuminate</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Database</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Eloquent</span><span style="color: #F286C4; font-style: italic">\</span><span style="color: #97E1F1; font-style: italic">Model</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F286C4">class</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1">Post</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">extends</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Model</span></span>
<span class="line"><span style="color: #F6F6F4">{</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">HasFactory</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Filterable</span><span style="color: #F6F6F4">;</span></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">use</span><span style="color: #F6F6F4"> </span><span style="color: #97E1F1; font-style: italic">Sortable</span><span style="color: #F6F6F4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">protected</span><span style="color: #F6F6F4"> $filterFields </span><span style="color: #F286C4">=</span><span style="color: #F6F6F4"> [</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">id</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">title</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">content</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">published_at</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">category_id</span><span style="color: #DEE492">&#39;</span><span style="color: #F6F6F4">,</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #DEE492">&#39;</span><span style="color: #E7EE98">category</span><span style="color: #DEE492">&#39;</span></span>
<span class="line"><span style="color: #F6F6F4">    ];</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F6F6F4">    </span><span style="color: #F286C4">public</span><span style="color: #F6F6F4"> </span><span style="color: #F286C4">function</span><span style="color: #F6F6F4"> </span><span style="color: #62E884">category</span><span style="color: #F6F6F4">()</span></span>
<span class="line"><span style="color: #F6F6F4">    {</span></span>
<span class="line"><span style="color: #F6F6F4">        </span><span style="color: #F286C4">return</span><span style="color: #F6F6F4"> </span><span style="color: #BF9EEE; font-style: italic">$this</span><span style="color: #F286C4">-&gt;</span><span style="color: #62E884">belongsTo</span><span style="color: #F6F6F4">(</span><span style="color: #97E1F1; font-style: italic">Category</span><span style="color: #F286C4">::class</span><span style="color: #F6F6F4">);</span></span>
<span class="line"><span style="color: #F6F6F4">    }</span></span>
<span class="line"><span style="color: #F6F6F4">}</span></span>
<span class="line"></span></code></pre></div>



<p>Các bạn chỉ cần Use Filterable và Sortable của thư viện để sử dụng. Mảng $filterFields là những giá trị mình muốn filter nhé. Ở đây mình có Category là relation của Post.</p>



<p>Tiếp theo trong function của mình dùng câu lệnh này nữa là oke nhoé :))</p>



<pre class="wp-block-code"><code>return Post::sort()->filter()->with('category')->get();</code></pre>



<p>Đây là một url để chúng ta filters nhé</p>



<pre class="wp-block-code"><code>GET /api/posts?filters&#91;field]&#91;operator]=value</code></pre>



<p>Dưới đây là một số operator mà thư viện hỗ trợ</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="799" height="1024" src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-799x1024.png" alt="" class="wp-image-2485" srcset="https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-799x1024.png 799w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-234x300.png 234w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-768x984.png 768w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-380x487.png 380w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM-800x1025.png 800w, https://blog.tomosia.com.vn/wp-content/uploads/2023/12/Screenshot-2023-12-10-at-4.38.25-PM.png 1118w" sizes="auto, (max-width: 799px) 100vw, 799px" /></figure>



<h2 id="test-ket-qua" class="wp-block-heading">Test kết quả</h2>



<p>Dưới đây là video mình test bằng Postman. Các bạn có thể test thử xem nhé!</p>



<figure class="wp-block-video"><video height="1740" style="aspect-ratio: 2866 / 1740;" width="2866" controls src="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/demo-purity.mov"></video></figure>



<h2 id="tong-ket" class="wp-block-heading">Tổng kết</h2>



<p>Đây là một thư viện khá hay mà mình vô tình đọc được. Ở trên mình tập trung nhiều cho phần <strong><em>filter</em></strong>, còn <strong><em>sort</em></strong> cũng khá hay, các bạn khám phá thử xem nhá.</p>



<p>Hy vọng các bạn cảm thấy nó thú vị và có thể áp dụng vào dự án nếu có những tính năng tương tự. Cảm ơn mọi người đã dành thời gian đọc ạ ☺️☺️☺️</p>



<p><em>Nguồn:</em> </p>



<ul class="wp-block-list">
<li>Trang chủ của thư viện <a href="https://abbasudo.github.io/laravel-purity/">https://abbasudo.github.io/laravel-purity/</a></li>



<li><a href="https://laravel-news.com/filter-api-responses-with-laravel-purity?ref=dailydev">https://laravel-news.com/filter-api-responses-with-laravel-purity?ref=dailydev</a></li>
</ul>



<p></p>
<p>The post <a href="https://blog.tomosia.com.vn/sort-va-filter-du-lieu-de-dang-hon-voi-laravel-purity/">Sort và filter dữ liệu dễ dàng hơn với Laravel Purity</a> appeared first on <a href="https://blog.tomosia.com.vn">Tomoshare</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.tomosia.com.vn/sort-va-filter-du-lieu-de-dang-hon-voi-laravel-purity/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		<enclosure url="http://blog.tomosia.com.vn/wp-content/uploads/2023/12/demo-purity.mov" length="38785537" type="video/quicktime" />

			</item>
	</channel>
</rss>
