Membina aplikasi berbilang penyewa dengan batu madu/konteks

PHPz
Lepaskan: 2024-08-12 15:11:25
asal
737 orang telah melayarinya

Jangan dikelirukan dengan perpustakaan konteks baharu Laravel, pakej ini boleh digunakan untuk membina aplikasi berbilang penyewa berbilang konteks. Kebanyakan perpustakaan berbilang penyewa pada asasnya mempunyai satu konteks 'penyewa', jadi jika anda memerlukan berbilang konteks, perkara boleh menjadi agak rumit. Pakej baru ni selesaikan masalah tu.

Mari kita lihat satu contoh ya?

Contoh projek

Untuk aplikasi contoh kami, kami akan mempunyai pangkalan pengguna global yang disusun dalam pasukan dan setiap pasukan akan mempunyai berbilang projek. Ini adalah struktur yang agak biasa dalam kebanyakan aplikasi Perisian sebagai Perkhidmatan.

Adalah perkara biasa bagi aplikasi berbilang penyewa untuk mempunyai setiap pangkalan pengguna dalam konteks penyewa, tetapi untuk aplikasi contoh kami, kami mahu pengguna dapat menyertai berbilang pasukan, jadi asas pengguna global itu.
Rajah asas pengguna global vs penyewa

Building a multi-tenant application with honeystone/context

Sebagai SaaS, kemungkinan besar pasukan akan menjadi entiti yang boleh dibilkan (iaitu tempat duduk) dan ahli pasukan tertentu akan diberikan kebenaran untuk mengurus pasukan. Saya tidak akan menyelami butiran pelaksanaan ini dalam contoh ini, tetapi semoga ia memberikan beberapa konteks tambahan.

Pemasangan

Untuk memastikan siaran ini ringkas, saya tidak akan menerangkan cara memulakan projek Laravel anda. Terdapat banyak sumber yang lebih baik tersedia untuk itu, tidak terkecuali dokumentasi rasmi. anggap saja anda sudah mempunyai projek Laravel, dengan model Pengguna, Pasukan dan Projek, dan anda sudah bersedia untuk mula melaksanakan pakej konteks kami.

Pemasangan adalah pujian komposer ringkas:

composer install honeystone/context
Salin selepas log masuk

Pustaka ini mempunyai fungsi kemudahan, context(), yang pada Laravel 11 bertembung dengan fungsi konteks Laravel sendiri. Ini bukan masalah sebenarnya. Anda boleh sama ada mengimport fungsi kami:

use function Honestone\Context\context;
Salin selepas log masuk

Atau hanya gunakan bekas suntikan kebergantungan Laravel. Sepanjang siaran ini saya akan menganggap anda telah mengimport fungsi dan menggunakannya dengan sewajarnya.

model-model

Mari mulakan dengan mengkonfigurasi model Pasukan kami:

belongsToMany(User::class); } public function projects(): HasMany { return $this->hasMany(Project::class); } }
Salin selepas log masuk

Sebuah pasukan mempunyai nama, ahli dan projek. Dalam aplikasi kami, hanya ahli pasukan akan dapat mengakses pasukan atau projeknya.

Baiklah, mari kita lihat Projek kami:

belongsTo(Team::class); } }
Salin selepas log masuk

Sesuatu projek mempunyai nama dan tergolong dalam pasukan.

Menentukan konteks

Apabila seseorang mengakses aplikasi kami, kami perlu menentukan pasukan dan projek yang mereka bekerjasama. Untuk memastikan perkara ini mudah, mari kita kendalikan perkara ini dengan parameter laluan. Kami juga akan menganggap bahawa hanya pengguna yang disahkan boleh mengakses aplikasi.

Konteks pasukan mahupun projek:app.mysaas.dev
Konteks pasukan sahaja:app.mysaas.dev/my-team
Konteks pasukan dan projek:app.mysaas.dev/my-team/my-project

Laluan kami akan kelihatan seperti ini:

Route::middleware('auth')->group(function () { Route::get('/', DashboardController::class); Route::middleware(AppContextMiddleware::Class)->group(function () { Route::get('/{team}', TeamController::class); Route::get('/{team}/{project}', ProjectController::class); }); });
Salin selepas log masuk

Ini adalah pendekatan yang sangat tidak fleksibel, memandangkan potensi pertembungan ruang nama, tetapi ia mengekalkan contoh yang ringkas. Dalam aplikasi dunia nyata, anda mungkin mahu mengendalikan perkara ini sedikit berbeza, mungkin anothersaas.dev/teams/my-team/projects/my-project atau my-team.anothersas.dev/projects/my-project.

Kita harus melihat AppContextMiddleware kami terlebih dahulu. Perisian tengah ini memulakan konteks pasukan dan, jika ditetapkan, konteks projek:

route('team'); $request->route()->forgetParameter('team'); $projectId = null; //if there's a project, pull that too if ($request->route()->hasParamater('project')) { $projectId = $request->route('project'); $request->route()->forgetParameter('project'); } //initialise the context context()->initialize(new AppResolver($teamId, $projectId)); } }
Salin selepas log masuk

Untuk bermula, kami mengambil id pasukan dari laluan dan kemudian melupakan parameter laluan. Kami tidak memerlukan parameter mencapai pengawal kami sebaik sahaja ia berada dalam konteks. Jika id projek ditetapkan, kami juga menariknya. Kami kemudiannya memulakan konteks menggunakan AppResolver kami yang melepasi id pasukan kami dan id projek kami (atau nol):

require('team', Team::class) ->accept('project', Project::class); } public function resolveTeam(): ?Team { return Team::with('members')->find($this->teamId); } public function resolveProject(): ?Project { return $this->projectId ?: Project::with('team')->find($this->projectId); } public function checkTeam(DefinesContext $definition, Team $team): bool { return $team->members->find(context()->auth()->getUser()) !== null; } public function checkProject(DefinesContext $definition, ?Project $project): bool { return $project === null || $project->team->id === $this->teamId; } public function deserialize(array $data): self { return new static($data['team'], $data['project']); } }
Salin selepas log masuk

Sedikit lagi berlaku di sini.

Kaedah define() bertanggungjawab untuk menentukan konteks yang sedang diselesaikan. Pasukan itu diperlukan dan mestilah model Pasukan dan projek itu diterima (iaitu pilihan) dan mestilah model Projek (atau batal).

resolveTeam() akan dipanggil secara dalaman pada permulaan. Ia mengembalikan Pasukan atau batal. Sekiranya tindak balas nol, CouldNotResolveRequiredContextException akan dilemparkan oleh ContextInitializer.

resolveProject() juga akan dipanggil secara dalaman pada permulaan. Ia mengembalikan Projek atau null. Dalam kes ini, respons batal tidak akan menghasilkan pengecualian kerana projek tidak diperlukan oleh definisi.

Selepas menyelesaikan pasukan dan projek, ContextInitializer akan memanggil kaedah checkTeam() dan checkProject() pilihan. Kaedah ini menjalankan pemeriksaan integriti. Untuk checkTeam() kami memastikan bahawa pengguna yang disahkan ialah ahli pasukan dan untuk checkProject() kami menyemak sama ada projek itu milik pasukan.

Finally, every resolver needs a deserialization() method. This method is used to reinstate a serialised context. Most notably this happens when the context is used in a queued job.

Now that our application context is set, we should use it.

Accessing the context

As usual, we’ll keep it simple, if a little contrived. When viewing the team we want to see a list of projects. We could build our TeamController to handle this requirements like this:

projects; return view('team', compact('projects')); } }
Salin selepas log masuk

Easy enough. The projects belonging to the current team context are passed to our view. Imagine we now need to query projects for a more specialised view. We could do this:

id) ->where('name', 'like', "%$query%") ->get(); return view('queried-projects', compact('projects')); } }
Salin selepas log masuk

It’s getting a little fiddly now, and it’s far too easy to accidentally forget to ‘scope’ the query by team. We can solve this using the BelongsToContext trait on our Project model:

belongsTo(Team::class); } }
Salin selepas log masuk

All project queries will now be scooped by the team context and the current Team model will be automatically injected into new Project models.

Let’s simplify that controller:

get(); return view('queried-projects', compact('projects')); } }
Salin selepas log masuk

That’s all folks

From here onwards, you’re just building your application. The context is easily at hand, your queries are scoped and queued jobs will automagically have access to the same context from which they were dispatched.

Not all context related problems are solved though. You’ll probably want to create some validation macros to give your validation rules a little context, and don’t forget manual queries will not have the context automatically applied.

If you’re planning to use this package in your next project, we’d love to hear from you. Feedback and contribution is always welcome.

You can checkout the GitHub repository for additional documentation. If you find our package useful, please drop a star.

Until next time..


This article was originally posted to the Honeystone Blog. If you like our articles, consider checking our more of our content over there.

Atas ialah kandungan terperinci Membina aplikasi berbilang penyewa dengan batu madu/konteks. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!