Les transactions de base de données sont essentielles si vous souhaitez effectuer quelques opérations d’insertion/mise à jour/suppression de base de données et garantir l’intégrité des données. Jetons un coup d’œil à 5 exemples différents de projets open source Laravel.
1. themsaid/ergodnc : créer Office avec des balises
Le code du Contrôleur :
class OfficeController extends Controller
{
public function create(): JsonResource
{
$attributes = (new OfficeValidator())->validate(
$office = new Office(),
request()->all()
);
$attributes['approval_status'] = Office::APPROVAL_PENDING;
$attributes['user_id'] = auth()->id();
$office = DB::transaction(function () use ($office, $attributes) {
$office->fill(
Arr::except($attributes, ['tags'])
)->save();
if (isset($attributes['tags'])) {
$office->tags()->attach($attributes['tags']);
}
return $office;
});
// ...
}
}
Comme vous pouvez le voir, le Office
un nouvel enregistrement modèle est créé, puis, avec la relation plusieurs-à-plusieurs ->attach()
DB Transaction garantit que si l’attachement de la balise échoue, le nouvel enregistrement Office est également annulé.
Voir le code original sur GitHub.
2. JuanDMeGon/Laravel-from-Scratch : plusieurs à plusieurs avec blocages
Le code du Contrôleur :
class OrderController extends Controller
{
public function store(Request $request)
{
return DB::transaction(function() use($request) {
$user = $request->user();
$order = $user->orders()->create([
'status' => 'pending',
]);
$cart = $this->cartService->getFromCookie();
$cartProductsWithQuantity = $cart
->products
->mapWithKeys(function ($product) {
$quantity = $product->pivot->quantity;
if ($product->stock < $quantity) {
throw ValidationException::withMessages([
'cart' => "There is not enough stock for the quantity you required of {$product->title}",
]);
}
$product->decrement('stock', $quantity);
$element[$product->id] = ['quantity' => $quantity];
return $element;
});
$order->products()->attach($cartProductsWithQuantity->toArray());
return redirect()->route('orders.payments.create', ['order' => $order->id]);
}, 5);
}
}
Discutons de la transaction qui se passe ici et pourquoi.
- Nous créons le
Order
- Puis on décrémente le stock de
$product
- Enfin, nous attachons des produits à la
$order
Chose intéressante ici : saviez-vous que vous pouvez ajouter un paramètre numérique à la DB::transaction()
méthode?
Voir au dessus:
DB::transaction(function() use($request) {
// ... whatever is inside
}, 5);
Qu’est ce que ça 5
faire?
Citation des documents originaux de Laravel :
Le transaction
La méthode accepte un deuxième argument facultatif qui définit le nombre de fois qu’une transaction doit être réessayée lorsqu’un blocage se produit. Une fois ces tentatives épuisées, une exception sera levée.
Voir le code original sur GitHub.
3. amitavroy/doctor-app
Le code de la classe Service :
class PatientService
{
public function createPatient($patientData): Patient
{
$settingService = app()->make(SettingService::class);
try {
DB::beginTransaction();
$patientNumber = $settingService->getNextPatientNumber();
$patient = Patient::create([
'patient_id' => now()->format('Ym') . $patientNumber,
'name' => $patientData['name'],
'phone_number' => $patientData['phone_number'],
'year_of_birth' => now()->subYears($patientData['age'])->format('Y'),
'weight' => $patientData['weight'],
]);
$settingService->incrementLastPatientNumber();
DB::commit();
return $patient;
} catch (Exception $exception) {
logger()->error($exception->getMessage());
}
}
}
Celui-ci est intéressant : il utilise beginTransaction()
et commit()
manuellement, et encapsule également la transaction dans un try .. catch
déclaration, en plus.
Voir le code original sur GitHub.
4. pixelfed/pixelfed : supprimez tout en toute sécurité
Le code de la classe Job :
class DeleteRemoteProfilePipeline implements ShouldQueue
{
// ...
public function handle()
{
$profile = $this->profile;
DB::transaction(function() use ($profile) {
$profile->avatar->forceDelete();
$id = $profile->id;
MediaTag::whereProfileId($id)->delete();
StatusHashtag::whereProfileId($id)->delete();
DirectMessage::whereFromId($id)->delete();
FollowRequest::whereFollowingId($id)
->orWhere('follower_id', $id)
->forceDelete();
Follower::whereProfileId($id)
->orWhere('following_id', $id)
->forceDelete();
Like::whereProfileId($id)->forceDelete();
});
}
}
C’est assez simple. Il y a tellement de choses liées au profil, il y a donc de grandes chances d’échec si certains de ces enregistrements fonctionnent mal pendant le processus de suppression. Donc DB Transaction est une évidence ici.
Il se passe plus de choses dans ce Job, alors consultez le code original sur GitHub.
5. akaunting / akaunting
Le code d’une méthode de classe PHP Action personnalisée :
class Reconciliations extends BulkAction
{
public function reconcile($request)
{
$reconciliations = $this->getSelectedRecords($request);
foreach ($reconciliations as $reconciliation) {
\DB::transaction(function () use ($reconciliation) {
$reconciliation->reconciled = 1;
$reconciliation->save();
Transaction::where('account_id', $reconciliation->account_id)
->isNotReconciled()
->whereBetween('paid_at', [$reconciliation->started_at, $reconciliation->ended_at])->each(function ($item) {
$item->reconciled = 1;
$item->save();
});
});
}
}
}
La réconciliation est un terme financier pour “fusionner des enregistrements transactionnels” ou quelque chose comme ça.
Nous devons donc marquer le rapprochement comme enregistré, mais également marquer les transactions individuelles comme rapprochées.
Certains d’entre vous demanderaient pourquoi il y a un ->each()
fonction pour chaque transaction, au lieu d’une simple mise à jour groupée ? Je suppose qu’il y a des observateurs ou d’autres événements qui sont automatiquement déclenchés sur le individuel mise à jour de l’enregistrement, qui ne serait pas exécutée automatiquement autrement.
Voir le code original sur GitHub.
Vous voulez plus d’exemples de code ?
Donc, ce ne sont que 5 exemples. Nous avons collecté des centaines d’exemples de code de projets open source Laravel.
Vous pouvez consulter quelques exemples de transactions DB supplémentaires ou parcourir toute la liste des exemples de code par sujet/balise.