استعلامات متقدمة — Supabase
الاستعلامات المتقدمة و RPC
فلاتر مركبة
import { supabase } from "./supabaseClient.js";
export async function searchProducts({ term, min, max }) {
let query = supabase.from("products").select("id, name, price, category");
if (term) query = query.ilike("name", `%${term}%`);
if (min !== undefined) query = query.gte("price", min);
if (max !== undefined) query = query.lte("price", max);
const { data, error } = await query.order("price");
if (error) throw error;
return data;
}بناء الاستعلام تدريجياً حسب الفلاتر الموجودة نمط شائع. .or("price.lt.50,featured.eq.true") لشروط "أو"، و .in("category", [...]) لقائمة. ⚠️ لا تلصق مدخلات المستخدم داخل نص .or(...) بلا تحقق — صيغته نصية ويمكن التلاعب بها.
العلاقات المتداخلة
بالمفاتيح الأجنبية، يجلب select الجداول المرتبطة في طلب واحد:
import { supabase } from "./supabaseClient.js";
export async function getPost(postId) {
const { data, error } = await supabase
.from("posts")
.select("id, title, author:profiles(id, name), comments(id, content, created_at)")
.eq("id", postId)
.single();
if (error) throw error;
return data;
}author:profiles(...) اسم مستعار للعلاقة. RLS تُطبق على كل جدول مرتبط أيضاً.
الترقيم والعد
import { supabase } from "./supabaseClient.js";
export async function page(n, size = 20) {
const from = (n - 1) * size;
const { data, count, error } = await supabase
.from("products")
.select("id, name", { count: "exact" })
.range(from, from + size - 1); // شامل للطرفين
if (error) throw error;
return { data, total: count };
}RPC: دوال PostgreSQL
للمنطق الذي يحتاج عدة عمليات في معاملة واحدة، أو حسابات معقدة، اكتب دالة SQL واستدعها بـ supabase.rpc("name", args):
CREATE OR REPLACE FUNCTION get_user_stats(p_user_id uuid)
RETURNS json
LANGUAGE sql STABLE
SECURITY INVOKER -- تعمل بصلاحيات المستدعي: RLS تُطبق
AS $$
SELECT json_build_object(
'posts', (SELECT count(*) FROM posts WHERE author_id = p_user_id),
'comments', (SELECT count(*) FROM comments WHERE user_id = p_user_id)
);
$$;⚠️ لاحظ اسم المعامل p_user_id: لو سُمّي user_id لكان الشرط WHERE user_id = user_id يقارن العمود بنفسه (صحيح دائماً) ويعدّ كل التعليقات — خطأ موجود في النسخة السابقة من هذا الدرس. استخدم بادئة للمعاملات.
SECURITY DEFINER بحذر
دالة SECURITY DEFINER تعمل بصلاحيات منشئها وتتجاوز RLS. استخدمها فقط عند الحاجة، مع: فحص auth.uid() داخلها، و SET search_path = ''، وأسماء جداول كاملة (public.posts)، وسحب صلاحية التنفيذ من anon إن لم تكن للعموم.
تمرين
اكتب دالة RPC place_order(p_items jsonb) تتحقق من المخزون وتنقصه وتنشئ الطلب في معاملة واحدة، وتستخدم auth.uid() كمالك للطلب بدل قيمة من العميل.
import { supabase } from "./supabaseClient.js";
// فلاتر اختيارية تُبنى تدريجياً
export async function searchProducts({ term, minPrice, maxPrice, categories } = {}) {
let query = supabase.from("products").select("id, name, price, category", { count: "exact" });
if (term) query = query.ilike("name", `%${term}%`);
if (minPrice !== undefined) query = query.gte("price", minPrice);
if (maxPrice !== undefined) query = query.lte("price", maxPrice);
if (categories?.length) query = query.in("category", categories);
const { data, count, error } = await query.order("price").range(0, 19);
if (error) throw error;
return { data, total: count };
}
// منشور مع كاتبه وتعليقاته في طلب واحد
export async function getPostWithComments(postId) {
const { data, error } = await supabase
.from("posts")
.select("id, title, content, author:profiles(id, name, avatar_url), comments(id, content, created_at, user:profiles(name))")
.eq("id", postId)
.single();
if (error) throw error;
return data;
}
// استدعاء دالة PostgreSQL
export async function getMyStats(userId) {
const { data, error } = await supabase.rpc("get_user_stats", { p_user_id: userId });
if (error) throw error;
return data; // { posts: 3, comments: 12 }
}
/* ===== الدالة في SQL Editor — بادئة p_ تمنع حجب اسم العمود =====
CREATE OR REPLACE FUNCTION get_user_stats(p_user_id uuid)
RETURNS json LANGUAGE sql STABLE SECURITY INVOKER AS $$
SELECT json_build_object(
'posts', (SELECT count(*) FROM posts WHERE author_id = p_user_id),
'comments', (SELECT count(*) FROM comments WHERE user_id = p_user_id)
);
$$;
*/اختبر فهمك