FiveM x Prisma ORM – انجمن DEV
Summarize this content to 400 words in Persian Lang
⚠️ ما در مورد سرور JavaScript/TypeScript FiveM صحبت می کنیم ⚠️⚠️
FiveM یک تغییر برای Grand Theft Auto V است که به شما امکان میدهد به صورت چند نفره روی سرورهای اختصاصی سفارشیسازی شده با Cfx.re بازی کنید.
وقتی یک سرور FiveM را توسعه میدهید، استفاده از پایگاه داده برای ذخیره دادههای پخشکننده و همه اطلاعات پایدار دیگر کلاسیک است.
برای استفاده از پایگاه داده در کد سمت سرور، جامعه FiveM چندین اتصال ایجاد کرد:
fivem-async-mysql
oxmysql
…
همه این کانکتورها روش هایی را برای اجرای پرس و جوهای خام ارائه می دهند مانند:
const response = await MySQL.query(‘SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?’, [
identifier
])
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
2 مشکل اصلی در این روش تعامل با پایگاه داده وجود دارد:
Dev باید پاسخ را تجزیه کند تا آن را با یک مدل خوب تایپ کند
پرس و جو در مواجهه با طرحواره پایگاه داده قبل از اجرا تایید نمی شود
این نقاط درد با استفاده از ORM حل می شوند، آنها به شما اجازه می دهند مدل های خود را توصیف کنید، بررسی نوع را مدیریت می کند. در این مقاله نحوه ادغام Prisma ORM را در FiveM خواهیم دید.
پوشه منبع FiveM که در مقاله خود استفاده خواهم کرد یک فضای کاری pnpm است. این اجازه می دهد تا چندین بسته را با کد مشترک مدیریت کنید.
├── player-manager
│ └── prisma
│ │ ├── player.schema.prisma
│ │ └── package.json // @player-manager/prisma package
│ └── server
│ │ ├── server.ts
│ │ └── package.json // @player-manager/server package
│ └── fxmanifest.lua
├── lib
│ └── prisma-orm. // @lib/prisma-orm package
│ ├── package.json
│ └── …
├── package.json
└── pnpm-worspace.yml
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
طرحواره منشور را تقسیم کنید
در prisma-orm راه اندازی پوشه Prisma پس از شروع. ابتدا، ما طرحواره Prisma را به چند منبع تقسیم می کنیم.
برای این کار از ابزار merge-prisma-schema استفاده می کنیم. از آن برای ادغام قطعات طرحواره در ماژول های چندگانه استفاده می شود
merge-prisma-schema.config.ts
const config = {
schemas: [
“header.prisma”,
“@player-manager/prisma/player.schema.prisma”,
],
output: “prisma/schema.prisma”,
schemaSearchFolders: [“node_modules”, “prisma”],
prismaCli: “npx prisma”,
};
export default config;
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
header.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = “prisma-client-js”
engineType = “binary”
binaryTargets = [“windows”]
output = “C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/prisma-orm/prisma/generated”
}
datasource db {
provider = “sqlite”
url = “file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db”
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
داخل @player-module فایل player.schema.prisma
model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
به روز رسانی prisma-orm ماژول package.json
{
“name”: “@lib/database”,
“version”: “1.0.0”,
“scripts”: {
“——— BUILD ———“: “——— BUILD ———“,
“pregenerate”: “npx merge-prisma-schema && rm -rf ./prisma/generated”,
“generate”: “prisma generate”,
“——— MIGRATE ———“: “——— MIGRATE ———“,
“premigrate”: “npx merge-prisma-schema”,
“migrate”: “prisma migrate”
},
“devDependencies”: {
“@player-manager/prisma”: “workspace:*”,
“merge-prisma-schema”: “^1.0.0”,
“prisma”: “catalog:”,
“ts-node”: “catalog:”,
“typescript”: “catalog:”,
},
“dependencies”: {
“@prisma/client”: “catalog:”
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
اگر بدوید npx pregenerate آن را به روز می کند prisma.schema فایل مانند این:
// Generated at 2024-09-07T20:46:01.740Z by “merge-prisma-schema”
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = “prisma-client-js”
engineType = “binary”
binaryTargets = [“windows”]
output = “C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/database/prisma/generated”
}
datasource db {
provider = “sqlite”
url = “file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db”
}
model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
زمان اجرا منشور را ایجاد کنید
استفاده خواهیم کرد npx generate برای ایجاد زمان اجرا Prisma، همانطور که در مستندات توضیح داده شده است. پس از تولید، از یک اسکریپت برای اصلاح فایل استفاده می کنیم index.js که تولید شده است. برای انجام آن از بسته استفاده می شود tsx.
package.json
{
“name”: “@lib/prisma-orm”,
“version”: “1.0.0”,
“scripts”: {
“——— BUILD ———“: “——— BUILD ———“,
“pregenerate”: “npx merge-prisma-schema && rm -rf ./prisma/generated”,
“generate”: “prisma generate”,
“postgenerate”: “npx tsx patch-generated-runtime.ts”,
“——— MIGRATE ———“: “——— MIGRATE ———“,
“premigrate”: “npx merge-prisma-schema”,
“migrate”: “prisma migrate”
},
“devDependencies”: {
“@player-manager/prisma”: “workspace:*”,
“merge-prisma-schema”: “^1.0.0”,
“prisma”: “catalog:”,
“ts-node”: “catalog:”,
“typescript”: “catalog:”,
},
“dependencies”: {
“@prisma/client”: “catalog:”
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
patch-generated-runtime.ts
import { readFileSync, writeFileSync } from “fs”;
import { join } from “path”;
const toReplace = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, ‘schema.prisma’))
}) ?? alternativePaths[0]
config.dirname = path.join(process.cwd(), alternativePath)
config.isBundled = true
`;
const replaceValue = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, ‘schema.prisma’))
});
if(alternativePath) {
config.dirname = path.join(process.cwd(), alternativePath)
} else if(config?.generator?.output?.value) {
config.dirname = config.generator.output.value;
} else {
config.dirname = path.join(process.cwd(), alternativePaths[0]);
}
config.isBundled = true
`;
const file = join(process.cwd(), ‘prisma’, ‘generated’, ‘index.js’);
const indexContent = readFileSync(file, { encoding: ‘utf-8’});
const newIndexContent = indexContent.replace(toReplace, replaceValue);
writeFileSync(file, newIndexContent);
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
با پچ، دایرکتوری Prisma با عبارت تعریف می شود output اموال تنظیم شده در schema.prisma
بسته نرم افزاری prisma client
همانطور که FiveM فقط با آن کار می کند commonjs فایل های همراه، ما استفاده خواهیم کرد rollup برای ایجاد یک کد ساخته شده سازگار. این کد با بسته ها/منابع دیگر به اشتراک گذاشته خواهد شد.
rollup.config.mjs
import typescript from “@rollup/plugin-typescript”;
import commonjs from “@rollup/plugin-commonjs”;
import resolve from “@rollup/plugin-node-resolve”;
const banner = `
const { resolve, join } = require(“path”);
const { cwd } = require(“process”);
var __dirname = resolve();
var __filename = join(__dirname, “index.js”);
process.env[“PRISMA_QUERY_ENGINE_BINARY”] = join(cwd(), “resources”, “lib”, “prisma-orm”, “prisma”, “generated”, “query-engine-windows.exe”);
`;
export default {
input: “./index.ts”,
output: {
dir: “dist”,
format: “cjs”,
sourcemap: false,
banner,
},
plugins: [resolve(), typescript(), commonjs()],
};
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
با این پیکربندی، یک بسته نرم افزاری ایجاد می کنیم و پیشوند آن را با برخی از اطلاعات مهم اضافه می کنیم:
__dirname و __filename در زمینه اجرای گره FiveM وجود ندارد، بنابراین ما آن را به صورت دستی تعریف می کنیم زیرا Prisma به آن نیاز دارد.
process.env[“PRISMA_QUERY_ENGINE_BINARY”] محل اجرای موتور پرس و جو است که در پوشه زمان اجرا Prisma ایجاد شده است
نمونه استفاده
داخل @player-manager/server که یک بسته Typescript با پیکربندی rollup است که در اینجا توضیح داده شده است، اضافه می کنم @lib/prisma-orm به dependencies.
server.ts
import { prisma } from “@lib/database”
on(“onResourceStart”, async (resName: string) => {
if (resName === GetCurrentResourceName()) {
const players = await prisma.player.findMany();
console.log(“players”, JSON.stringify(players))
}
});
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
می توانید یک مثال کامل را در اینجا بازیابی کنید https://github.com/JustinMartinDev/experimental-rp
⚠️ ما در مورد سرور JavaScript/TypeScript FiveM صحبت می کنیم ⚠️⚠️
FiveM یک تغییر برای Grand Theft Auto V است که به شما امکان میدهد به صورت چند نفره روی سرورهای اختصاصی سفارشیسازی شده با Cfx.re بازی کنید.
وقتی یک سرور FiveM را توسعه میدهید، استفاده از پایگاه داده برای ذخیره دادههای پخشکننده و همه اطلاعات پایدار دیگر کلاسیک است.
برای استفاده از پایگاه داده در کد سمت سرور، جامعه FiveM چندین اتصال ایجاد کرد:
- fivem-async-mysql
- oxmysql
- …
همه این کانکتورها روش هایی را برای اجرای پرس و جوهای خام ارائه می دهند مانند:
const response = await MySQL.query('SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ?', [
identifier
])
2 مشکل اصلی در این روش تعامل با پایگاه داده وجود دارد:
- Dev باید پاسخ را تجزیه کند تا آن را با یک مدل خوب تایپ کند
- پرس و جو در مواجهه با طرحواره پایگاه داده قبل از اجرا تایید نمی شود
این نقاط درد با استفاده از ORM حل می شوند، آنها به شما اجازه می دهند مدل های خود را توصیف کنید، بررسی نوع را مدیریت می کند. در این مقاله نحوه ادغام Prisma ORM را در FiveM خواهیم دید.
پوشه منبع FiveM که در مقاله خود استفاده خواهم کرد یک فضای کاری pnpm است. این اجازه می دهد تا چندین بسته را با کد مشترک مدیریت کنید.
├── player-manager
│ └── prisma
│ │ ├── player.schema.prisma
│ │ └── package.json // @player-manager/prisma package
│ └── server
│ │ ├── server.ts
│ │ └── package.json // @player-manager/server package
│ └── fxmanifest.lua
├── lib
│ └── prisma-orm. // @lib/prisma-orm package
│ ├── package.json
│ └── ...
├── package.json
└── pnpm-worspace.yml
طرحواره منشور را تقسیم کنید
در prisma-orm
راه اندازی پوشه Prisma پس از شروع. ابتدا، ما طرحواره Prisma را به چند منبع تقسیم می کنیم.
برای این کار از ابزار merge-prisma-schema استفاده می کنیم. از آن برای ادغام قطعات طرحواره در ماژول های چندگانه استفاده می شود
merge-prisma-schema.config.ts
const config = {
schemas: [
"header.prisma",
"@player-manager/prisma/player.schema.prisma",
],
output: "prisma/schema.prisma",
schemaSearchFolders: ["node_modules", "prisma"],
prismaCli: "npx prisma",
};
export default config;
header.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
engineType = "binary"
binaryTargets = ["windows"]
output = "C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/prisma-orm/prisma/generated"
}
datasource db {
provider = "sqlite"
url = "file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db"
}
داخل @player-module
فایل player.schema.prisma
model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}
به روز رسانی prisma-orm
ماژول package.json
{
"name": "@lib/database",
"version": "1.0.0",
"scripts": {
"--------- BUILD ---------": "--------- BUILD ---------",
"pregenerate": "npx merge-prisma-schema && rm -rf ./prisma/generated",
"generate": "prisma generate",
"--------- MIGRATE ---------": "--------- MIGRATE ---------",
"premigrate": "npx merge-prisma-schema",
"migrate": "prisma migrate"
},
"devDependencies": {
"@player-manager/prisma": "workspace:*",
"merge-prisma-schema": "^1.0.0",
"prisma": "catalog:",
"ts-node": "catalog:",
"typescript": "catalog:",
},
"dependencies": {
"@prisma/client": "catalog:"
}
}
اگر بدوید npx pregenerate
آن را به روز می کند prisma.schema
فایل مانند این:
// Generated at 2024-09-07T20:46:01.740Z by "merge-prisma-schema"
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
engineType = "binary"
binaryTargets = ["windows"]
output = "C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/lib/database/prisma/generated"
}
datasource db {
provider = "sqlite"
url = "file:C:/gta-fivem/txData/CFXDefaultFiveM_8AE314.base/resources/dev.db"
}
model Player {
id Int @id @default(autoincrement())
steamId String @unique
lastname String
firstname String
inventory Inventory?
}
زمان اجرا منشور را ایجاد کنید
استفاده خواهیم کرد npx generate
برای ایجاد زمان اجرا Prisma، همانطور که در مستندات توضیح داده شده است. پس از تولید، از یک اسکریپت برای اصلاح فایل استفاده می کنیم index.js
که تولید شده است. برای انجام آن از بسته استفاده می شود tsx
.
package.json
{
"name": "@lib/prisma-orm",
"version": "1.0.0",
"scripts": {
"--------- BUILD ---------": "--------- BUILD ---------",
"pregenerate": "npx merge-prisma-schema && rm -rf ./prisma/generated",
"generate": "prisma generate",
"postgenerate": "npx tsx patch-generated-runtime.ts",
"--------- MIGRATE ---------": "--------- MIGRATE ---------",
"premigrate": "npx merge-prisma-schema",
"migrate": "prisma migrate"
},
"devDependencies": {
"@player-manager/prisma": "workspace:*",
"merge-prisma-schema": "^1.0.0",
"prisma": "catalog:",
"ts-node": "catalog:",
"typescript": "catalog:",
},
"dependencies": {
"@prisma/client": "catalog:"
}
}
patch-generated-runtime.ts
import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
const toReplace = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, 'schema.prisma'))
}) ?? alternativePaths[0]
config.dirname = path.join(process.cwd(), alternativePath)
config.isBundled = true
`;
const replaceValue = `
const alternativePath = alternativePaths.find((altPath) => {
return fs.existsSync(path.join(process.cwd(), altPath, 'schema.prisma'))
});
if(alternativePath) {
config.dirname = path.join(process.cwd(), alternativePath)
} else if(config?.generator?.output?.value) {
config.dirname = config.generator.output.value;
} else {
config.dirname = path.join(process.cwd(), alternativePaths[0]);
}
config.isBundled = true
`;
const file = join(process.cwd(), 'prisma', 'generated', 'index.js');
const indexContent = readFileSync(file, { encoding: 'utf-8'});
const newIndexContent = indexContent.replace(toReplace, replaceValue);
writeFileSync(file, newIndexContent);
با پچ، دایرکتوری Prisma با عبارت تعریف می شود output
اموال تنظیم شده در schema.prisma
بسته نرم افزاری prisma client
همانطور که FiveM فقط با آن کار می کند commonjs
فایل های همراه، ما استفاده خواهیم کرد rollup
برای ایجاد یک کد ساخته شده سازگار. این کد با بسته ها/منابع دیگر به اشتراک گذاشته خواهد شد.
rollup.config.mjs
import typescript from "@rollup/plugin-typescript";
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
const banner = `
const { resolve, join } = require("path");
const { cwd } = require("process");
var __dirname = resolve();
var __filename = join(__dirname, "index.js");
process.env["PRISMA_QUERY_ENGINE_BINARY"] = join(cwd(), "resources", "lib", "prisma-orm", "prisma", "generated", "query-engine-windows.exe");
`;
export default {
input: "./index.ts",
output: {
dir: "dist",
format: "cjs",
sourcemap: false,
banner,
},
plugins: [resolve(), typescript(), commonjs()],
};
با این پیکربندی، یک بسته نرم افزاری ایجاد می کنیم و پیشوند آن را با برخی از اطلاعات مهم اضافه می کنیم:
-
__dirname
و__filename
در زمینه اجرای گره FiveM وجود ندارد، بنابراین ما آن را به صورت دستی تعریف می کنیم زیرا Prisma به آن نیاز دارد. -
process.env["PRISMA_QUERY_ENGINE_BINARY"]
محل اجرای موتور پرس و جو است که در پوشه زمان اجرا Prisma ایجاد شده است
نمونه استفاده
داخل @player-manager/server
که یک بسته Typescript با پیکربندی rollup است که در اینجا توضیح داده شده است، اضافه می کنم @lib/prisma-orm
به dependencies
.
server.ts
import { prisma } from "@lib/database"
on("onResourceStart", async (resName: string) => {
if (resName === GetCurrentResourceName()) {
const players = await prisma.player.findMany();
console.log("players", JSON.stringify(players))
}
});
می توانید یک مثال کامل را در اینجا بازیابی کنید https://github.com/JustinMartinDev/experimental-rp