Sillä Sen avulla kehittäjät voivat irrottaa liiketoimintaprosessien logiikan entiteettitilasta, muuntamalla monimutkaiset "spagettikoodin" tilan tarkistukset puhtaiksi, visualisoitaviksi suunnattuiksi kaavioiksi. symfony/workflow Vuosien ajan komponentilla oli kuitenkin tiukka rajoitus, joka perustui Petri-verkkojen käyttöönottoon: Objekti oli joko paikassa (tila), tai se ei ollut. Vaikka voisit olla useissa paikoissa samanaikaisesti (parallelinen käsittely), et voi olla samassa paikassa useita kertoja. Tokens were boolean Symfony 7.4 changes the game with Weighted Transitions. Uusi ominaisuus tuo esiin Voit nyt mallintaa skenaarioita, joissa määrät ovat tärkeitä: ”kerää 4 allekirjoitusta”, ”prosessoi 5 erän kohteita” tai ”odota, että 3 osajärjestelmää aloitetaan”. multiplicity Tässä artikkelissa rakennamme vahvan Tutkimme, miten voit määrittää painotetut siirtymät, toteuttaa entiteettilogian ja tarkistaa virtauksen tiukalla testauksella – kaikki Symfony 7.4:n ja PHP 8.3:n avulla. Multi-Signature Approval System The Concept (Petri Nets vs. valtion koneet) Ennen koodin kirjoittamista on tärkeää ymmärtää, miksi tämä ominaisuus on olemassa. Valtion koneen rajoittaminen Valtion kone on lineaarinen, hissi on myös ja tai Se ei voi olla Tämä on täydellinen yksinkertaisemmille tiloille (esim. ) on STOPPED MOVING_UP MOVING_DOWN MOVING_UP Order::STATUS_PAID Työnkulku (Petri Net) A on mahdollistaa kohteen istumisen useisiin paikkoihin samanaikaisesti. ”Uusi työntekijä” -prosessissa työntekijä voi olla samanaikaisesti: Workflow Käyttöliittymä - Laptop Luo sähköpostiosoite / tili Molempien on oltava valmiita ennen kuin ne siirtyvät . onboarded Puuttuva osa: Monipuolisuus Ennen Symfony 7.4:ää, jos tarvitsit ”3 johtajaa kustannusten hyväksymiseen”, et voinut mallia tätä pelkästään työnkulussa. Luo odottava_hyväksyttävä sijainti. Lisää entiteetillesi vastaava kenttä ($approvalCount). Guard Event -kuuntelijalla voit tarkistaa, onko ($subject->getApprovalCount() >= 3) ennen siirtymän sallimista. kanssa ”The” ” on nyt osa työnkulun tilaa itse. Työnkulun moottori ymmärtää natiivisesti, että aihe on Valitse 3 kertaa. Weighted Transitions counter approved Hankkeen asettaminen Luomme uuden Symfony-projektin ja asennamme tarvittavat komponentit. composer create-project symfony/skeleton:"7.4.*" fintech-approval cd fintech-approval composer require symfony/workflow symfony/framework-bundle symfony/orm-pack symfony/maker-bundle Tätä esimerkkiä varten käytämme yksinkertaisuuden vuoksi SQLiteä, mutta logiikka pätee MySQL/PostgreSQL:ään täsmälleen samalla tavalla. # .env DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" Ydinlogiikka (The Core Logic) Määrittelemme työnkulun nimeltä expense_approval. The Scenario: Kustannusraportti on laadittu (luonnos). Se on toimitettu (review_required) Painotettu vaihe: Järjestelmä jakaa pyynnön 3 vaaditulle hyväksyjälle. Jokainen hyväksyjä myöntää hyväksynnän erikseen. Kun kolme hyväksyntää on kerätty, kustannukset siirtyvät valmiiksi maksettavaksi. Luo tai päivitä : config/packages/workflow.yaml # config/packages/workflow.yaml framework: workflows: expense_approval: type: workflow # MUST be 'workflow', not 'state_machine' audit_trail: enabled: true marking_store: type: method property: currentState # This must hold an array supports: - App\Entity\ExpenseReport initial_marking: draft places: - draft - review_pool - approved_pool - ready_for_payment - rejected transitions: submit: from: draft to: - place: review_pool weight: 3 # <--- OUTPUT WEIGHT approve: from: review_pool to: approved_pool # Default weight is 1. One 'review_pool' token becomes one 'approved_pool' token. reject: from: review_pool to: rejected # If rejected, we might want to clear all tokens, # but for simplicity, one rejection moves to rejected. finalize: from: - place: approved_pool weight: 3 # <--- INPUT WEIGHT to: ready_for_payment Konfiguroinnin purkaminen Tyyppi: Työnkulku: Painotetut siirtymät perustuvat token-kuutioihin.Tämä ei ole mahdollista valtion koneessa. submitTransition:- to: {paikka: review_pool, paino: 3 } - Kun tämä syttyy, thereview_poolplace saa 3 tokenia.- Ajattele tätä luomalla3 "lippua", jotka täytyy lyödä. hyväksyäSiirtyminen:: review_pool, että: approved_pool.- Standard 1-to-1 paino.- Koska meillä on 3 tokeneja inreview_pool, voimme ampua tämän siirtymän 3 kertaa. FinalizeTransition:- alkaen: {paikka: hyväksytty_pool, paino: 3 }- Tämä siirtyminen on estetty, kunnes hyväksytty_pool sisältää täsmälleen (tai vähintään) 3 tokenia.- Kun 3rd hyväksyntä tulee, tämä polku avautuu. Entiteettiä Tarvitsemme yhteisön, joka tukee tätä "Multi-State" -merkkikauppaa. omaisuuden on oltava array, joka pitää token-laskelmat (esim. ) on currentState [‘review_pool’ => 2, ‘approved_pool’ => 1] namespace App\Entity; use App\Repository\ExpenseReportRepository; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ExpenseReportRepository::class)] class ExpenseReport { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 255)] private string $description; #[ORM\Column] private float $amount; /** * Stores the workflow state. * For Weighted Workflows, this stores the places and their quantities. * Example DB content: {"review_pool": 2, "approved_pool": 1} */ #[ORM\Column(type: 'json')] private array $currentState = []; public function __construct(string $description, float $amount) { $this->description = $description; $this->amount = $amount; // Initial marking is handled by the workflow component, // but initializing to empty array is good practice. } public function getId(): ?int { return $this->id; } public function getDescription(): string { return $this->description; } public function getAmount(): float { return $this->amount; } public function getCurrentState(): array { return $this->currentState; } public function setCurrentState(array $currentState): self { $this->currentState = $currentState; return $this; } } Muuttoliikkeen käynnistäminen: php bin/console make:migration php bin/console doctrine:migrations:migrate Palvelu Layer Jotta voimme vuorovaikutuksessa tämän työnkulun puhtaasti, meidän pitäisi luoda palvelu. Tämä palvelu käsittelee logiikkaa "kuka" hyväksyy, vaikka tässä opetusohjelmassa keskitymme työnkulun mekaniikkaan. namespace App\Service; use App\Entity\ExpenseReport; use Symfony\Component\Workflow\WorkflowInterface; use Symfony\Component\Workflow\Registry; readonly class ExpenseManager { public function __construct( private Registry $workflowRegistry, ) {} public function submit(ExpenseReport $expense): void { $workflow = $this->getWorkflow($expense); if ($workflow->can($expense, 'submit')) { $workflow->apply($expense, 'submit'); } else { throw new \LogicException('Cannot submit this expense report.'); } } public function approve(ExpenseReport $expense): void { $workflow = $this->getWorkflow($expense); // In a real app, you would check "Is the current user one of the allowed approvers?" here. if ($workflow->can($expense, 'approve')) { $workflow->apply($expense, 'approve'); // Check if we can auto-finalize (if 3 approvals are met) if ($workflow->can($expense, 'finalize')) { $workflow->apply($expense, 'finalize'); } } else { throw new \LogicException('Approval not needed or not allowed.'); } } public function getStatus(ExpenseReport $expense): array { // returns something like ['review_pool' => 2, 'approved_pool' => 1] return $expense->getCurrentState(); } private function getWorkflow(ExpenseReport $expense): WorkflowInterface { return $this->workflowRegistry->get($expense, 'expense_approval'); } } ”Automaattinen” logiikka Huomaa hyväksymismenetelmä. hyväksymisen soveltamisen jälkeen tarkistamme välittömästi voi ($kustannukset, ‘lopeta’). FirstApproval:-review_pool: 2-approved_pool: 1-finalize needs 3 approved_pool. can(‘finalize’) palauttaa väärän. SecondApproval:-review_pool: 1-approved_pool: 2-can(‘finalize’) palauttaa väärän. ThirdApproval:review_pool: 0approved_pool: 3finalize needs 3. can(‘finalize’) palauttaa true.We applyfinalize.New State:ready_for_payment: 1. Työnkulun visualisointi Ennen testausta on erittäin hyödyllistä visualisoida kaavio, varsinkin jos siihen liittyy painoja. php bin/console workflow:dump expense_approval | dot -Tpng -o workflow.png Jos sinulla ei ole sitä, voit liittää tekstin tuloksen online Graphviz-katseluun. Lähtö edustaa visuaalisesti nuolia, joissa on merkinnät kuten paino: 3, mikä tekee selväksi, että lähettämäsi siirtymä synnyttää useita tokeneja. Yksikön testaus (Unit Testing) Emme vain toivo, että se toimii; me todistamme sen. Voit ladata todellisen työnkulun konfiguraation ja testata siirtymät. KernelTestCase //tests/Workflow/ExpenseApprovalWorkflowTest.php namespace App\Tests\Workflow; use App\Entity\ExpenseReport; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Workflow\WorkflowInterface; class ExpenseApprovalWorkflowTest extends KernelTestCase { private WorkflowInterface $workflow; protected function setUp(): void { self::bootKernel(); $container = static::getContainer(); $registry = $container->get('workflow.registry'); // We create a dummy subject to get the workflow // In a real app, passing the class name to the registry is preferred if supported, // or fetching by name directly if you have a custom service alias. $subject = new ExpenseReport('Test', 100.0); $this->workflow = $registry->get($subject, 'expense_approval'); } public function testWeightedApprovalFlow(): void { $expense = new ExpenseReport('MacBook Pro', 3000.00); // 1. Initial State $this->assertTrue($this->workflow->can($expense, 'submit')); $this->workflow->apply($expense, 'submit'); // Verify tokens: Should be in review_pool 3 times $marking = $expense->getCurrentState(); $this->assertArrayHasKey('review_pool', $marking); $this->assertEquals(3, $marking['review_pool'], 'Should have 3 pending reviews'); // 2. First Approval $this->assertTrue($this->workflow->can($expense, 'approve')); $this->workflow->apply($expense, 'approve'); $marking = $expense->getCurrentState(); $this->assertEquals(2, $marking['review_pool']); $this->assertEquals(1, $marking['approved_pool']); // Check that we CANNOT finalize yet (need 3 approvals) $this->assertFalse($this->workflow->can($expense, 'finalize'), 'Should not finalize with only 1 approval'); // 3. Second Approval $this->workflow->apply($expense, 'approve'); $marking = $expense->getCurrentState(); $this->assertEquals(1, $marking['review_pool']); $this->assertEquals(2, $marking['approved_pool']); // 4. Third Approval $this->workflow->apply($expense, 'approve'); $marking = $expense->getCurrentState(); // At this specific moment, review_pool is 0, approved_pool is 3 $this->assertEquals(0, $marking['review_pool'] ?? 0); $this->assertEquals(3, $marking['approved_pool']); // 5. Finalize $this->assertTrue($this->workflow->can($expense, 'finalize'), 'Should be able to finalize now'); $this->workflow->apply($expense, 'finalize'); // 6. Verify End State $marking = $expense->getCurrentState(); $this->assertArrayHasKey('ready_for_payment', $marking); $this->assertEquals(1, $marking['ready_for_payment']); // Previous tokens should be consumed $this->assertArrayNotHasKey('approved_pool', $marking); } } Tee tämä testi: php bin/phpunit tests/Workflow/ExpenseApprovalWorkflowTest.php Edistykselliset käyttötavat ja parhaat käytännöt Käyttämällä Enums for Places (Uusi 7.4) Symfony 7.4 lisää myös tukea varmuuskopioiduille yksiköille työnkulkuissa. ”Sinun pitäisi määritellä yksilö. review_pool namespace App\Enum; enum ExpenseState: string { case DRAFT = 'draft'; case REVIEW_POOL = 'review_pool'; case APPROVED_POOL = 'approved_pool'; case READY = 'ready_for_payment'; case REJECTED = 'rejected'; } Voit sitten päivittää työnkulkuasi.yaml (vaikka YAML käyttää edelleen sarakkeita, PHP-koodi voi käyttää ) on ExpenseState::REVIEW_POOL->value Tapahtumat, joissa on painoja Saatat haluta estää saman kolminkertainen siirtymävaihtoehto, joka mahdollistaa siirtymävaiheen 3 kertaa, mutta työnkulun moottori ei luonnostaan tiedä, kuka sen soitti. person approve Tämän ratkaisemiseksi käytä vartijan kuuntelijaa. #[AsEventListener('workflow.expense_approval.guard.approve')] public function preventDoubleApproval(GuardEvent $event): void { /** @var ExpenseReport $expense */ $expense = $event->getSubject(); $user = $this->security->getUser(); // Imagine the entity has a list of who already approved if ($expense->hasApproved($user)) { $event->setBlocked(true, 'You have already approved this expense.'); } } Käsittelyn hylkääminen (Token Cleanup) Yksi haaste painotettujen tokenien kanssa on puhdistus. Jos meillä on 2 hyväksyntää ja kolmas henkilö soittaa , mitä tapahtuu kahdelle istuvalle tokenille ? reject approved_pool Normaalissa työnkulussa siirrytään Voi jättää ne Tokenit kaatuvat (luomalla zombie-tila, jossa raportti on sekä hylätty että osittain hyväksytty). rejected approved_pool Sillä Siirtymävaiheen tulisi ihanteellisesti kuluttaa Kuitenkin dynaamista kulutusta ei tueta pelkästään YAML: ssä (et voi sanoa "kuluttaa KAIKKI"). reject all Tapahtuman tilaajan käyttäminen , voit manuaalisesti palauttaa merkinnän myymälän. workflow.entered.rejected public function onRejected(Event $event): void { $expense = $event->getSubject(); // Force reset state to ONLY rejected $expense->setCurrentState(['rejected' => 1]); } Johtopäätös Sinfonia 7.4 Näytelmät Sallimalla useiden tilan esimerkkien (tokenien) olemassaolon samanaikaisesti voimme nyt mallinntaa äänestysjärjestelmiä, valmistaa kokoonpanolinjoja ja eränkäsittelylogiikkaa suoraan kokoonpanoon, mikä vähentää huomattavasti mukautetun PHP-kattilan määrää. Weighted Workflow Transitions Key Takeaways: Moninaisuus: Paikat voivat pitää useita tokeneja, mikä mahdollistaa valtion rinnakkaisen kertymisen. Konfiguraatio: Käytä painoa määritelmissäsi To (Output) ja From (Input) ohjata token-virtausta. Tallennus: Varmista, että Marking Store -ominaisuutesi on array (esimerkiksi JSON-sarakkeen) jäljittääksesi tunnisteiden määrän sijaintia kohden. Todentaminen: Käytä aina työnkulkua:dump-komentoa ja kirjoita KernelTestCase-testejä graafisen logiikan todistamiseksi ennen käyttöönottoa. Tämä ominaisuus vahvistaa Symfony Workflow -komponenttia johtavana PHP-ratkaisuna valtionhallinnassa, jonka avulla voit poistaa hauraat "counter" -ominaisuudet ja luottaa matemaattisesti luotettavaan arkkitehtuuriin. Olkaamme kosketuksissa Näiden kehittyneiden mallien hyväksyminen voi merkittävästi yksinkertaistaa verkkotunnuksesi logiikkaa, mutta siirtyminen ei ole aina ilmeistä.Jos haluat modernisoida Symfony-pinoasi, tarvitset toisen silmän arkkitehtuurillesi tai haluat vain pelätä uusimpia Petri-verkon toteutuksia, haluaisin kuulla sinulta. Ota yhteyttä minuun LinkedInissä ( ) jatkaa keskustelua nykyaikaisesta PHP-arkkitehtuurista. https://www.linkedin.com/in/matthew-mochalkin/ https://www.linkedin.com/in/matthew-mochalkin/