[{"data":1,"prerenderedAt":888},["ShallowReactive",2],{"article:\u002Farticles\u002Fclean-architecture-for-php-applications":3,"article-surround:\u002Farticles\u002Fclean-architecture-for-php-applications":879},{"id":4,"title":5,"body":6,"date":867,"description":868,"draft":869,"extension":870,"meta":871,"navigation":261,"path":872,"seo":873,"stem":874,"tags":875,"__hash__":878},"articles\u002Farticles\u002Fclean-architecture-for-php-applications.md","Clean Architecture for PHP Applications",{"type":7,"value":8,"toc":865},"minimark",[9,13,64,67,861],[10,11,12],"p",{},"Good architecture is crucial for applications of all sizes. Follow me as I explore how to create a multi-layered architecture for a PHP application that not only promotes code organisation but also enhances maintainability. Here’s a look at how we can structure our code for clarity and efficiency. The following example utilises the (in)famous Repository pattern to separate the Data layer and includes a Service layer for business logic, then a Controller layer for user input and presentation. You might notice that it is very similar to the MVC (Model-View-Controller ) pattern but with an added 'service' layer for business logic. Let's discuss the different layers:",[14,15,16,29,42,55],"ul",{},[17,18,19,23,24,28],"li",{},[20,21,22],"strong",{},"The Repository Layer"," (",[25,26,27],"code",{},"UserRepository.php","): This is where all our data is handled. We query our database to fetch user details, acting as the sole gatekeeper for data access. This separation ensures our data logic stays neat and tidy.",[17,30,31,23,34,37,38,41],{},[20,32,33],{},"The Service Layer",[25,35,36],{},"UserService.php","): It fetches the data via ",[25,39,40],{},"UserRepository"," and processes it, applying business logic to transform raw data into meaningful information. This layer bridges the gap between data and action, ensuring our business rules are encapsulated correctly.",[17,43,44,23,47,50,51,54],{},[20,45,46],{},"Data Transfer Objects (DTOs)",[25,48,49],{},"UserDTO.php","): Carries our data. DTOs assist in transferring data across layers, ensuring a type-safe way of interacting with information. Each ",[25,52,53],{},"UserDTO"," carries user details to our application layers.",[17,56,57,23,60,63],{},[20,58,59],{},"The Controller Layer",[25,61,62],{},"UserController.php","): This layer handles user requests, calls upon the necessary services to get the data, and presents it to the user. This layer is about retrieving and manipulating data to present to the user.",[10,65,66],{},"Together, these components form a symphony of clean code and architecture, making our application not just a pleasure to work with but also a robust platform ready to scale. 🌟",[68,69,74],"pre",{"className":70,"code":71,"language":72,"meta":73,"style":73},"language-php shiki shiki-themes github-dark github-light","\u002F\u002F App\\Repositories\\UserRepository.php\nclass UserRepository extends Repository\n{\n    \u002F**\n     * Get users\n     *\n     * @return array|bool\n     *\u002F\n    public function get(): array|bool\n    {\n        return $this\n            ->database\n            ->query('SELECT id, name FROM users')\n            ->get();\n    }\n}\n\n\u002F\u002F App\\Services\\UserService.php\nclass UserService\n{\n    \u002F**\n     * @var UserRepository\n     *\u002F\n    private UserRepository $user_repo;\n\n    public function __construct(UserRepository $user_repo)\n    {\n        $this->user_repo = $user_repo;\n    }\n\n    \u002F**\n     * Get users\n     *\n     * @return array\u003CUserDTO>|bool\n     *\u002F\n    public function get(): array|bool\n    {\n        $rows = $this->user_repo->get();\n        $results = [];\n\n        if (!$rows) {\n            return false;\n        }\n\n        foreach ($rows as $row) {\n            $result[] = new UserDTO(...$row);\n        }\n\n        return $results;\n    }\n}\n\n\u002F\u002F App\\DTOs\\UserDTO.php\nclass UserDTO\n{\n    public function __construct(\n        public readonly int $id,\n        public readonly string $name\n    ) {\n        \u002F\u002F ...\n    }\n}\n\n\u002F\u002F App\\Http\\Controllers\\UserController.php\nclass UserController extends BaseController\n{\n    \u002F**\n     * @var UserService\n     *\u002F\n    private UserService $user_service;\n\n    public function __construct(UserService $user_service)\n    {\n        $this->user_service = $user_service;\n    }\n\n    public function __invoke()\n    {\n        $users = $this\n            ->user_service\n            ->get();\n\n        return view('users.view.php', [\n            'users' => $users\n        ]);\n    }\n}\n","php","",[25,75,76,85,102,109,115,121,127,145,151,175,181,191,200,233,244,250,256,263,269,277,282,287,298,303,314,319,336,341,358,363,368,373,378,383,402,407,426,431,453,464,469,483,495,501,506,521,543,548,553,561,566,571,576,582,590,595,607,622,635,641,647,652,657,662,668,681,686,691,700,705,716,721,738,743,757,762,767,780,785,795,803,812,817,833,845,851,856],{"__ignoreMap":73},[77,78,81],"span",{"class":79,"line":80},"line",1,[77,82,84],{"class":83},"si27w","\u002F\u002F App\\Repositories\\UserRepository.php\n",[77,86,88,92,96,99],{"class":79,"line":87},2,[77,89,91],{"class":90},"spKkM","class",[77,93,95],{"class":94},"sqoU-"," UserRepository",[77,97,98],{"class":90}," extends",[77,100,101],{"class":94}," Repository\n",[77,103,105],{"class":79,"line":104},3,[77,106,108],{"class":107},"shWlK","{\n",[77,110,112],{"class":79,"line":111},4,[77,113,114],{"class":83},"    \u002F**\n",[77,116,118],{"class":79,"line":117},5,[77,119,120],{"class":83},"     * Get users\n",[77,122,124],{"class":79,"line":123},6,[77,125,126],{"class":83},"     *\n",[77,128,130,133,136,139,142],{"class":79,"line":129},7,[77,131,132],{"class":83},"     * ",[77,134,135],{"class":90},"@return",[77,137,138],{"class":90}," array",[77,140,141],{"class":83},"|",[77,143,144],{"class":90},"bool\n",[77,146,148],{"class":79,"line":147},8,[77,149,150],{"class":83},"     *\u002F\n",[77,152,154,157,160,163,166,169,171,173],{"class":79,"line":153},9,[77,155,156],{"class":90},"    public",[77,158,159],{"class":90}," function",[77,161,162],{"class":94}," get",[77,164,165],{"class":107},"()",[77,167,168],{"class":90},":",[77,170,138],{"class":90},[77,172,141],{"class":107},[77,174,144],{"class":90},[77,176,178],{"class":79,"line":177},10,[77,179,180],{"class":107},"    {\n",[77,182,184,187],{"class":79,"line":183},11,[77,185,186],{"class":90},"        return",[77,188,190],{"class":189},"sTU5a"," $this\n",[77,192,194,197],{"class":79,"line":193},12,[77,195,196],{"class":90},"            ->",[77,198,199],{"class":107},"database\n",[77,201,203,205,208,211,215,218,221,224,227,230],{"class":79,"line":202},13,[77,204,196],{"class":90},[77,206,207],{"class":94},"query",[77,209,210],{"class":107},"(",[77,212,214],{"class":213},"skb7c","'",[77,216,217],{"class":90},"SELECT",[77,219,220],{"class":213}," id, ",[77,222,223],{"class":90},"name",[77,225,226],{"class":90}," FROM",[77,228,229],{"class":213}," users'",[77,231,232],{"class":107},")\n",[77,234,236,238,241],{"class":79,"line":235},14,[77,237,196],{"class":90},[77,239,240],{"class":94},"get",[77,242,243],{"class":107},"();\n",[77,245,247],{"class":79,"line":246},15,[77,248,249],{"class":107},"    }\n",[77,251,253],{"class":79,"line":252},16,[77,254,255],{"class":107},"}\n",[77,257,259],{"class":79,"line":258},17,[77,260,262],{"emptyLinePlaceholder":261},true,"\n",[77,264,266],{"class":79,"line":265},18,[77,267,268],{"class":83},"\u002F\u002F App\\Services\\UserService.php\n",[77,270,272,274],{"class":79,"line":271},19,[77,273,91],{"class":90},[77,275,276],{"class":94}," UserService\n",[77,278,280],{"class":79,"line":279},20,[77,281,108],{"class":107},[77,283,285],{"class":79,"line":284},21,[77,286,114],{"class":83},[77,288,290,292,295],{"class":79,"line":289},22,[77,291,132],{"class":83},[77,293,294],{"class":90},"@var",[77,296,297],{"class":189}," UserRepository\n",[77,299,301],{"class":79,"line":300},23,[77,302,150],{"class":83},[77,304,306,309,311],{"class":79,"line":305},24,[77,307,308],{"class":90},"    private",[77,310,95],{"class":189},[77,312,313],{"class":107}," $user_repo;\n",[77,315,317],{"class":79,"line":316},25,[77,318,262],{"emptyLinePlaceholder":261},[77,320,322,324,326,329,331,333],{"class":79,"line":321},26,[77,323,156],{"class":90},[77,325,159],{"class":90},[77,327,328],{"class":189}," __construct",[77,330,210],{"class":107},[77,332,40],{"class":189},[77,334,335],{"class":107}," $user_repo)\n",[77,337,339],{"class":79,"line":338},27,[77,340,180],{"class":107},[77,342,344,347,350,353,356],{"class":79,"line":343},28,[77,345,346],{"class":189},"        $this",[77,348,349],{"class":90},"->",[77,351,352],{"class":107},"user_repo ",[77,354,355],{"class":90},"=",[77,357,313],{"class":107},[77,359,361],{"class":79,"line":360},29,[77,362,249],{"class":107},[77,364,366],{"class":79,"line":365},30,[77,367,262],{"emptyLinePlaceholder":261},[77,369,371],{"class":79,"line":370},31,[77,372,114],{"class":83},[77,374,376],{"class":79,"line":375},32,[77,377,120],{"class":83},[77,379,381],{"class":79,"line":380},33,[77,382,126],{"class":83},[77,384,386,388,390,392,395,397,400],{"class":79,"line":385},34,[77,387,132],{"class":83},[77,389,135],{"class":90},[77,391,138],{"class":90},[77,393,394],{"class":83},"\u003C",[77,396,53],{"class":189},[77,398,399],{"class":83},">|",[77,401,144],{"class":90},[77,403,405],{"class":79,"line":404},35,[77,406,150],{"class":83},[77,408,410,412,414,416,418,420,422,424],{"class":79,"line":409},36,[77,411,156],{"class":90},[77,413,159],{"class":90},[77,415,162],{"class":94},[77,417,165],{"class":107},[77,419,168],{"class":90},[77,421,138],{"class":90},[77,423,141],{"class":107},[77,425,144],{"class":90},[77,427,429],{"class":79,"line":428},37,[77,430,180],{"class":107},[77,432,434,437,439,442,444,447,449,451],{"class":79,"line":433},38,[77,435,436],{"class":107},"        $rows ",[77,438,355],{"class":90},[77,440,441],{"class":189}," $this",[77,443,349],{"class":90},[77,445,446],{"class":107},"user_repo",[77,448,349],{"class":90},[77,450,240],{"class":94},[77,452,243],{"class":107},[77,454,456,459,461],{"class":79,"line":455},39,[77,457,458],{"class":107},"        $results ",[77,460,355],{"class":90},[77,462,463],{"class":107}," [];\n",[77,465,467],{"class":79,"line":466},40,[77,468,262],{"emptyLinePlaceholder":261},[77,470,472,475,477,480],{"class":79,"line":471},41,[77,473,474],{"class":90},"        if",[77,476,23],{"class":107},[77,478,479],{"class":90},"!",[77,481,482],{"class":107},"$rows) {\n",[77,484,486,489,492],{"class":79,"line":485},42,[77,487,488],{"class":90},"            return",[77,490,491],{"class":189}," false",[77,493,494],{"class":107},";\n",[77,496,498],{"class":79,"line":497},43,[77,499,500],{"class":107},"        }\n",[77,502,504],{"class":79,"line":503},44,[77,505,262],{"emptyLinePlaceholder":261},[77,507,509,512,515,518],{"class":79,"line":508},45,[77,510,511],{"class":90},"        foreach",[77,513,514],{"class":107}," ($rows ",[77,516,517],{"class":90},"as",[77,519,520],{"class":107}," $row) {\n",[77,522,524,527,529,532,535,537,540],{"class":79,"line":523},46,[77,525,526],{"class":107},"            $result[] ",[77,528,355],{"class":90},[77,530,531],{"class":90}," new",[77,533,534],{"class":189}," UserDTO",[77,536,210],{"class":107},[77,538,539],{"class":90},"...",[77,541,542],{"class":107},"$row);\n",[77,544,546],{"class":79,"line":545},47,[77,547,500],{"class":107},[77,549,551],{"class":79,"line":550},48,[77,552,262],{"emptyLinePlaceholder":261},[77,554,556,558],{"class":79,"line":555},49,[77,557,186],{"class":90},[77,559,560],{"class":107}," $results;\n",[77,562,564],{"class":79,"line":563},50,[77,565,249],{"class":107},[77,567,569],{"class":79,"line":568},51,[77,570,255],{"class":107},[77,572,574],{"class":79,"line":573},52,[77,575,262],{"emptyLinePlaceholder":261},[77,577,579],{"class":79,"line":578},53,[77,580,581],{"class":83},"\u002F\u002F App\\DTOs\\UserDTO.php\n",[77,583,585,587],{"class":79,"line":584},54,[77,586,91],{"class":90},[77,588,589],{"class":94}," UserDTO\n",[77,591,593],{"class":79,"line":592},55,[77,594,108],{"class":107},[77,596,598,600,602,604],{"class":79,"line":597},56,[77,599,156],{"class":90},[77,601,159],{"class":90},[77,603,328],{"class":189},[77,605,606],{"class":107},"(\n",[77,608,610,613,616,619],{"class":79,"line":609},57,[77,611,612],{"class":90},"        public",[77,614,615],{"class":90}," readonly",[77,617,618],{"class":90}," int",[77,620,621],{"class":107}," $id,\n",[77,623,625,627,629,632],{"class":79,"line":624},58,[77,626,612],{"class":90},[77,628,615],{"class":90},[77,630,631],{"class":90}," string",[77,633,634],{"class":107}," $name\n",[77,636,638],{"class":79,"line":637},59,[77,639,640],{"class":107},"    ) {\n",[77,642,644],{"class":79,"line":643},60,[77,645,646],{"class":83},"        \u002F\u002F ...\n",[77,648,650],{"class":79,"line":649},61,[77,651,249],{"class":107},[77,653,655],{"class":79,"line":654},62,[77,656,255],{"class":107},[77,658,660],{"class":79,"line":659},63,[77,661,262],{"emptyLinePlaceholder":261},[77,663,665],{"class":79,"line":664},64,[77,666,667],{"class":83},"\u002F\u002F App\\Http\\Controllers\\UserController.php\n",[77,669,671,673,676,678],{"class":79,"line":670},65,[77,672,91],{"class":90},[77,674,675],{"class":94}," UserController",[77,677,98],{"class":90},[77,679,680],{"class":94}," BaseController\n",[77,682,684],{"class":79,"line":683},66,[77,685,108],{"class":107},[77,687,689],{"class":79,"line":688},67,[77,690,114],{"class":83},[77,692,694,696,698],{"class":79,"line":693},68,[77,695,132],{"class":83},[77,697,294],{"class":90},[77,699,276],{"class":189},[77,701,703],{"class":79,"line":702},69,[77,704,150],{"class":83},[77,706,708,710,713],{"class":79,"line":707},70,[77,709,308],{"class":90},[77,711,712],{"class":189}," UserService",[77,714,715],{"class":107}," $user_service;\n",[77,717,719],{"class":79,"line":718},71,[77,720,262],{"emptyLinePlaceholder":261},[77,722,724,726,728,730,732,735],{"class":79,"line":723},72,[77,725,156],{"class":90},[77,727,159],{"class":90},[77,729,328],{"class":189},[77,731,210],{"class":107},[77,733,734],{"class":189},"UserService",[77,736,737],{"class":107}," $user_service)\n",[77,739,741],{"class":79,"line":740},73,[77,742,180],{"class":107},[77,744,746,748,750,753,755],{"class":79,"line":745},74,[77,747,346],{"class":189},[77,749,349],{"class":90},[77,751,752],{"class":107},"user_service ",[77,754,355],{"class":90},[77,756,715],{"class":107},[77,758,760],{"class":79,"line":759},75,[77,761,249],{"class":107},[77,763,765],{"class":79,"line":764},76,[77,766,262],{"emptyLinePlaceholder":261},[77,768,770,772,774,777],{"class":79,"line":769},77,[77,771,156],{"class":90},[77,773,159],{"class":90},[77,775,776],{"class":189}," __invoke",[77,778,779],{"class":107},"()\n",[77,781,783],{"class":79,"line":782},78,[77,784,180],{"class":107},[77,786,788,791,793],{"class":79,"line":787},79,[77,789,790],{"class":107},"        $users ",[77,792,355],{"class":90},[77,794,190],{"class":189},[77,796,798,800],{"class":79,"line":797},80,[77,799,196],{"class":90},[77,801,802],{"class":107},"user_service\n",[77,804,806,808,810],{"class":79,"line":805},81,[77,807,196],{"class":90},[77,809,240],{"class":94},[77,811,243],{"class":107},[77,813,815],{"class":79,"line":814},82,[77,816,262],{"emptyLinePlaceholder":261},[77,818,820,822,825,827,830],{"class":79,"line":819},83,[77,821,186],{"class":90},[77,823,824],{"class":94}," view",[77,826,210],{"class":107},[77,828,829],{"class":213},"'users.view.php'",[77,831,832],{"class":107},", [\n",[77,834,836,839,842],{"class":79,"line":835},84,[77,837,838],{"class":213},"            'users'",[77,840,841],{"class":90}," =>",[77,843,844],{"class":107}," $users\n",[77,846,848],{"class":79,"line":847},85,[77,849,850],{"class":107},"        ]);\n",[77,852,854],{"class":79,"line":853},86,[77,855,249],{"class":107},[77,857,859],{"class":79,"line":858},87,[77,860,255],{"class":107},[862,863,864],"style",{},"html pre.shiki code .si27w, html code.shiki .si27w{--shiki-default:#6A737D;--shiki-light:#6A737D}html pre.shiki code .spKkM, html code.shiki .spKkM{--shiki-default:#F97583;--shiki-light:#D73A49}html pre.shiki code .sqoU-, html code.shiki .sqoU-{--shiki-default:#B392F0;--shiki-light:#6F42C1}html pre.shiki code .shWlK, html code.shiki .shWlK{--shiki-default:#E1E4E8;--shiki-light:#24292E}html pre.shiki code .sTU5a, html code.shiki .sTU5a{--shiki-default:#79B8FF;--shiki-light:#005CC5}html pre.shiki code .skb7c, html code.shiki .skb7c{--shiki-default:#9ECBFF;--shiki-light:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":73,"searchDepth":87,"depth":87,"links":866},[],"2024-03-03","A multi-layered PHP architecture with Repository, Service, DTO, and Controller layers that keeps business logic clean, testable, and ready to scale.",false,"md",{},"\u002Farticles\u002Fclean-architecture-for-php-applications",{"title":5,"description":868},"articles\u002Fclean-architecture-for-php-applications",[72,876,877],"architecture","ddd","9BLRospK4RpgSDJwhKLOhvcoWjutkwpptq4ISHrVKj4",[880,884],{"title":881,"path":882,"stem":883,"children":-1},"How I Built a 65 Million Item Array in PHP... Kind Of","\u002Farticles\u002Fbuilding-a-65-million-item-array-in-php","articles\u002Fbuilding-a-65-million-item-array-in-php",{"title":885,"path":886,"stem":887,"children":-1},"PSR-4 - Autoloader","\u002Farticles\u002Fpsr4-autoloader","articles\u002Fpsr4-autoloader",1782332464129]