Elemental是從頭開始開發的PHP框架,可動態,用戶友好的編碼體驗。它結合了依賴注入之類的功能,並遵循MVC架構來簡化Web開發並改善代碼組織。它以對簡單性和靈活性的熱情設計,它邀請開發人員進入一個領域,在那裡他們可以進行無與倫比的控制,並對可以使用的工具有深刻的了解。
為了展示Elemental的功能,使用Elemental開發了一個名為Inkwell的完整平台。 Inkwell是一個獨特的空間,致力於講故事的純粹本質。與Elemental沒有外部依賴關係的目標一致,僅使用普通HTML,CSS,JS和PHP製作Inkwell。
可以隨意深入研究實時平台和相應的代碼庫。探索Inkwell的功能,以了解如何為自己的項目利用元素。
請參閱創建元素背後的靈感。
元素的設計目的是沒有附加弦。外部庫或框架沒有依賴性。目的是給開發人員一種真正的控制感,這是一個獨立探索和理解為框架提供動力的神奇物品的開放之門。
總體目標?讓開發人員充分擁抱並利用強大的抽象(例如DI容器,Orms,Middlewares等)的優雅。但是這是踢腳 - 元素不僅僅是指向道路。它正在遞給您揭開奧秘的鑰匙,使您能夠探索代碼中如何佈置這些抽象。
實際上,不僅鼓勵您走這條路,還鼓勵您冒險脫穎而出。潛入代碼庫,剖析抽象並了解其內部工作。隨意進行調整和實驗,因為元素不僅是一個框架,而且是塑造和塑造工具可供使用的開放邀請。因為編碼不應該是迷宮;這應該是一段旅程。讓我們一起旅行。
與其他框架不同,Elemental不依賴作曲家或外部庫。這就像克隆存儲庫並在系統上安裝了良好的OL'PHP一樣簡單。
打開終端並執行以下命令:
git clone https://github.com/aneesmuzzafer/elemental.git不用擔心包裝管理人員或依賴項 - 元素是從頭開始構建的,可以使您擺脫此類擔憂。
對於那些喜歡作曲家路線的人,創建一個新的Elemental應用程序只是一個命令:
composer create-project fragment/elemental sample-app這將使用composer.json文件生成一個項目。
一旦您的項目準備就緒,使用我們的命令行引擎蠟燭使用ignite命令來啟動Elemental Local Development Server:
cd sample-app
php candle ignite瞧!您的應用程序現在可以在http://127.0.0.1:8000上訪問。
我們已經照顧了基本設置,因此您可以專注於魔術。
讓附魔開始!
元素的最重要特徵是它用於管理類依賴和執行依賴注入的依賴性注入容器。
依賴注入是軟件開發中的一種設計模式,該模式涉及組件如何保持其依賴性。在傳統系統中,班級負責創建自己的依賴性。有了DI,創建和提供依賴項的責任就在班級之外移動。它們不是創建其依賴性的類,而是從外部來源“注入”類。
DI有助於實現鬆散耦合和更可維護的代碼。它通過允許每個班級專注於其特定功能而不必擔心如何創建或獲得其依賴性來促進關注點的分離。
依賴注射是稱為控制反轉(IOC)的更廣泛概念的特定實現。 IOC代表一個設計範式,其中程序的控制流倒置或移交給外部實體,容器或框架。
在Elemental中,當您使用依賴項注入(DI)時,如果類不依賴任何其他類,或者僅依賴於具體類(不是抽象的接口),則無需明確告訴DI容器如何創建該類的實例。 DI容器將自動弄清楚。
容器將嘗試創建類的實例,如果該類對其他類具有依賴性,則容器將遞歸地嘗試解決這些依賴關係。這個過程一直持續到所有必要的類都成功解決。因此,您不必手動指定如何創建每個類 - DI容器為您負責。
<?php
class MailService {
public function __construct ( private MailerAgent $ mailer ) {
}
}
// Inside some other class
class UserController {
public function sendMail ( MailService $ mailService )
{
$ mailService -> sendMail ();
}
}在這裡,通過在方法參數中鍵入MailService ,Elemental能夠解析該類並創建此類實例並將其傳遞給sendMail ,以便您可以使用它而不必擔心MailService類所需的依賴項。如您所見, MailService本身取決於其他一些類MailerAgent ,但是,Elemental負責解決幕後MailerAgent類,在創建它的實例並為您使用該實例的同時,將其傳遞給了MailService 。
“那麼,僅通過將類名稱鍵入elemental起作用,這種注入依賴性將在哪裡?”所有類constructor功能,所有controller methods和命令創建類的handle方法。
在幕後,Elemental通過查看已註冊的任何綁定,將類或接口解決到具體實例中。換句話說,為了明確說明如何解決特定類或接口的實例的框架,您需要使用Application實例上的bind方法,傳遞我們希望註冊的類或接口,以及返回類的實例:
app ()-> bind (MailService::class, function () {
// Run some logic, for example, decide on the mail agent to be passed to its constructor depending on some factors.
return new MailService ( app ()-> make (MailAgent::class));
});請注意,通常只有在需要運行一些其他邏輯以解決類別或需要將接口綁定到具體實現時,才需要綁定類。否則,Elemental將在不明確要求您綁定的情況下解決該類。
singleton方法將類或接口與容器結合,以確保僅解決一次。在初始分辨率之後,任何隨後的調用對相同綁定的容器的調用都將返回相同的對象實例。
app ()-> singleton (DatabaseConnection::class, function () {
return new DatabaseConnection ( ' localhost ' , ' username ' , ' password ' );
});
// Later in the code
$ databaseConnection1 = app ()-> make (DatabaseConnection::class);
$ databaseConnection2 = app ()-> make (DatabaseConnection::class);
// $databaseConnection1 and $databaseConnection2 will reference the same instance雖然在應用程序中的任何位置註冊綁定的任何位置是完全可以的,但是在應用程序進行引導時通常需要綁定它,以便應用程序的其他組件可以開始使用它。 Elemental提供了一個特殊的位置,可以註冊應用程序的所有綁定並執行您應用程序所需的任何其他自舉邏輯。這是AppBootstrapAppServiceProvider 。應用服務提供商包含register和boot方法。
在register方法中,您應該將事物綁定到依賴項注入容器中。但是,您不應該嘗試解決任何綁定。路由或在register方法中運行任何其他功能。否則,您可能會意外地使用尚未加載的容器中的服務。
在所有其他服務提供商已註冊後,調用此方法,授予對框架註冊的所有服務的訪問。您希望執行的任何初始化邏輯都應在此處放置。
<?php
namespace App Bootstrap ;
use App Services Auth ;
class AppServiceProvider
{
public function register (): void
{
app ()-> singleton (Auth::class, function () {
return new Auth ();
});
}
public function boot (): void
{
// Additional initialization logic can be placed here
}
}您可以使用make方法從DI容器中解析類實例。應用程序實例上的make方法接受您要解決的類或接口的名稱:
use App Services MailService ;
$ mailService = app ()-> make (MailService::class);您也可以直接在Application程序類上使用靜態方法instance獲得應用程序實例。
use Core Main Application ;
use App Services MailService ;
$ mailService = Application:: instance ()-> make (MailService::class);當試圖從代碼組件中解析類時, make方法特別有用,在這種代碼組件中不切實際地註入依賴項是不切實際的。在這種情況下,您可以明確要求應用程序的依賴項注入容器為您解決實例。
路由是在approutes.php文件中定義的,允許開發人員輕鬆註冊各種路由以處理不同的HTTP請求。
路由通過在路線外牆上的相關方法(例如Route::get()中調用路線,並涉及將URI模式指定為第一個參數。第二個參數可以是定義負責處理請求的控制器和方法的封閉或數組。
例如:
<?php
use App Controllers AuthController ;
use Core Facade Route ;
Route:: get ( " /settings " , function () {
// handling logic goes here
});
Route:: post ( " /register " , [AuthController::class, " register " ]);每當匹配請求URI時,執行相應的閉合或控制器方法,並生成響應並將其發送回瀏覽器。
您可以使用以下方法註冊對任何HTTP動詞響應的路由:
Route::get($uri, $callback);Route::post($uri, $callback);Route::put($uri, $callback);Route::patch($uri, $callback);Route::delete($uri, $callback);Route::options($uri, $callback); 有時,您需要在路線內捕獲URI的段。例如,您可能需要從URL捕獲用戶的ID。您可以通過定義路由參數來做到這一點:
Route:: get ( ' /user/{id} ' , function ( string $ id ) {
return ' User ' . $ id ;
});
Route:: get ( " /story/{id} " , function ( $ id ) { /*...*/ });您可以根據路由的要求定義盡可能多的路由參數:
Route:: post ( " story/edit/{id} " , [StoryController::class, " edit " ]);
Route:: get ( " story/{story_id}/comment/{comment_id} " , [StoryController::class, " comment " ]);這些也將傳遞到控制器方法中。
元素無縫處理您的控制器方法的必要依賴項。這允許您使用類型固定在回調簽名中指定路由所需的任何依賴項。 Elemental負責自動解決並將聲明的依賴項注入回調。
例如,如果您在回調中鍵入Hint CoreRequestRequest ,則Elemental確保當前的HTTP請求自動注入路由回調:
<?php
use Core Request Request ;
Route:: get ( ' /users ' , function ( Request $ request ) {
// ...
});您可以按任何順序將鍵入依賴項和路由參數放置。
當您將模型ID作為參數傳遞給路由或控制器操作時,典型方法涉及查詢數據庫以基於該ID獲取相應的模型。 Elemental通過路由模型綁定簡化了此過程,為將模型實例直接注入路由的方便方式。
例如,您可以選擇注入與給定ID相對應的整個用戶模型實例,而不是僅將用戶的ID注入您的路由中。
在路由或控制器操作的上下文中,使用類型礦體的變量名稱定義模型,這些名稱與路線中的特定段相匹配。例如:
use App Models User ;
Route:: get ( ' /users/{user} ' , function ( User $ user ) {
return $ user -> email ;
});有時,您可能希望使用id以外的列解決模型。為此,您可以在路由參數定義中指定列:
use App Models User ;
Route:: get ( ' /users/{user:email} ' , function ( User $ user ) {
return $ user ;
});在這種情況下,Elemental將無縫注入具有匹配請求URI相應值的電子郵件的模型實例。
當然,路由模型結合也可以與控制器方法一起使用。
如果在數據庫中找不到匹配的模型實例,則該應用將拋出ModelNotFoundException 。您可以處理此類例外,並控制該應用程序在ExceptionsHandler類中拋出的任何此類行為以及其他例外。稍後再詳細介紹。
使用Route::fallback方法,您可以定義一個路由,該路由在沒有其他路由匹配傳入請求時將執行。
Route:: fallback ( function () {
// ...
});route:list燭台命令將提供應用程序中定義的所有路由的列表:
php candle route:list與其合併路由文件中關閉中的所有請求處理邏輯,不如考慮通過“控制器”類構建此行為。控制器允許您組織相關的請求處理邏輯成一個凝聚力類。例如, UserController類可以管理與用戶相關的各種傳入請求,例如顯示,創建,更新和刪除用戶。這些控制器類通常存儲在app/Controllers目錄中。
要生成新的控制器,您可以運行build:controller蠟燭命令。
php candle build:controller UserController這將在app/Controllers目錄中生成一個名為“ usercontroller.php”的新文件。
控制器可能具有許多公共方法,這些方法將響應傳入的HTTP請求:
<?php
use App Services Auth ;
namespace App Controllers ;
class AuthController
{
public function showRegister ()
{
return view ( " Register " )-> withLayout ( " layouts.DashboardLayout " );
}
public function logout ()
{
Auth:: logout ();
redirect ( " / " );
}
}創建控制器類及其方法後,您可以定義通往控制器方法的路由,如下所示:
use App Controllers UserController ;
Route:: get ( " /register " , [AuthController::class, " showRegister " ]);當收到的請求與指定的路由URI匹配時,將調用AppControllersUserController類中的showRegister方法,該方法將接收相應的路由參數。
元素服務容器負責解決所有控制器的實例。因此,您可以在控制器的構造函數中使用類型構圖來指定其可能需要的任何依賴項。所陳述的依賴項將自動解決並註入控制器實例
<?php
namespace App Controllers ;
use Core Database Database ;
class UserController
{
/**
* Create a new controller instance.
*/
public function __construct (
public Database $ db ,
) {}
}除了通過構造函數注入依賴關係外,您還可以使用控制器方法中的依賴項使用類型模具。用於方法注入的常見用例是將CoreRequestRequest或任何服務實例注入您的控制器方法:
創建和管理控制器有效處理請求。
<?php
namespace App Controllers ;
use Core Request Request ;
use App Services Auth ;
class StoryController
{
public function create ( Request $ request )
{
$ data = $ request -> data ();
$ user = Auth:: user ();
$ story = Story:: create ([...]);
return redirect ( " /story/ $ story -> id " );
}
}f您的控制器方法可以從路由參數中預期輸入,您可以靈活地按任何順序列出您的參數。例如,考慮以下路由定義:
Route:: post ( " story/update/{id} " , [StoryController::class, " update " ]);您仍然可以鍵入CoreRequestRequest並通過定義控制器方法訪問您的id參數:
<?php
namespace App Controllers ;
use Core Request Request ;
class StoryController
{
public function update ( string $ id , Request $ request )
{
// Update $story
return redirect ( " /story/ $ story -> id " );
}
}Elemental中的CoreRequestRequest類提供了一種面向對象的方法,用於與您的應用程序管理的當前HTTP請求。它促進了提交的輸入,cookie和文件以及請求以及提交的文件的檢索。
為了通過依賴項注入獲取當前的HTTP請求實例,您可以在路由閉合或控制器方法中使用CoreRequestRequest類的類型知識。服務容器將自動注入傳入的請求實例。
<?php
namespace App Controllers ;
use App Models Category ;
use Core Request Request ;
class CategoryController
{
public function store ( Request $ request )
{
$ name = $ request -> data ()[ " name " ];
$ category = Category:: where ([ " name " => $ name ]);
if ( $ category ) {
return view ( " Category " , [ " categories " => Category:: all (), " msg " => " Category already exists! " ])-> withLayout ( " layouts.DashboardLayout " );
}
Category:: create ( $ request -> data ());
redirect ( " /category " );
}
}服務容器也將自動將傳入的請求注入路線關閉。
如果您的控制器方法可以從路由參數中預期輸入,則可以靈活地按任何順序列出您的參數。例如,考慮以下路由定義:
Route:: post ( " story/update/{id} " , [StoryController::class, " update " ]);您仍然可以鍵入CoreRequestRequest並通過定義控制器方法訪問您的id參數:
<?php
namespace App Controllers ;
use Core Request Request ;
class StoryController
{
public function update ( string $ id , Request $ request )
{
// Update $story
return redirect ( " /story/ $ story -> id " );
}
}您可以使用data()方法將所有傳入請求的輸入數據作為array獲取。無論輸入請求是來自HTML表單還是XHR請求:
$ data = $ request -> data ();您可以訪問Request實例中的所有用戶輸入,而不必擔心使用哪種HTTP動詞。無論使用HTTP動詞, data方法都可以用於檢索用戶輸入:
$ name = $ request -> data ()[ " name " ];CoreRequestRequest實例提供了各種檢查傳入HTTP請求的方法。讓我們討論以下一些最重要的方法。
您可以使用headers方法從CoreRequestRequest實例中檢索請求標題。
$ headers = $ request -> headers ();您可以通過在CoreRequestRequest實例上調用method來檢索請求方法。
$ method = $ request -> method ();您可以使用uri方法從CoreRequestRequest實例中檢索請求URI。
$ uri = $ request -> uri ();您可以使用cookies方法從CoreRequestRequest實例中檢索請求cookie。
$ cookies = $ request -> cookies ();您可以使用rawContent方法從CoreRequestRequest實例中檢索原始內容。
$ content = $ request -> rawContent ();處理請求的原始內容時要小心。
您可以使用files方法從CoreRequestRequest實例中檢索文件。
$ files = $ request -> files ();ip方法可用於檢索向您的應用程序提出請求的客戶端的IP地址:
$ ipAddress = $ request -> ip ();port方法可用於檢索向您的應用程序提出請求的客戶端地址:
$ port = $ request -> port ();您可以使用contentType方法從CoreRequestRequest實例中檢索內容類型。
$ contentType = $ request -> contentType ();您可以使用queryString方法檢索請求的查詢字符串。
$ query = $ request -> queryString ();您可以使用text方法檢索請求的文本內容,提供內容類型設置為text/plain
$ text = $ request -> text ();您可以使用js方法檢索請求的JS內容,前提是內容類型設置為application/javascript
$ js = $ request -> js ();您可以使用html方法檢索請求的HTML內容,前提是內容類型設置為text/html
$ js = $ request -> html ();您可以使用json方法檢索請求的JSON內容,前提是將內容類型設置為application/json $request->data()返回傳遞給請求的所有JSON數據。然而,
$ jsonData = $ request -> json (); $request->data()包含所有JSON數據以及通過請求中查詢參數傳遞的輸入。但是, $request->json()可用於僅檢索JSON內容。
您可以使用xml方法檢索請求的XML內容,前提是將內容類型設置為application/json
$ xmlData = $ request -> xml ();預計每個路線和控制器都會產生向用戶瀏覽器交付的響應。 Elemental提供了各種生成響應的方法。最簡單的響應形式涉及直接從路由或控制器返回字符串。該框架將無縫將此字符串無縫轉換為完整的HTTP響應。
Route:: get ( ' / ' , function () {
return ' Hello World ' ;
});除了從路由和控制器返回字符串外,您還可以返回數組或對象。該框架將自動將其轉換為JSON響應:
Route:: get ( ' / ' , function () {
return [ 1 , 2 , 3 ];
});通常,您不會僅僅從路線操作中返回直接的字符串或數組。相反,您通常會返回CoreResponseResponse或視圖的完整實例。
返回完整的Response實例使您可以自定義響應的HTTP狀態代碼和標題。您可以通過將響應實例鍵入控制器或路由閉合來注入響應實例。
use Core Response Response ;
Route:: get ( ' /home ' , function ( Response $ response ) {
$ response -> setHeader ( " content-type " , " text/plain " )
-> setStatusCode ( 200 )
-> setContent ( " Hello World " );
return $ response ;
});您當然可以從控制器返回view 。但是,如果您需要控制響應的狀態和標題,但還需要返回view作為響應的內容,則可以如下執行以下操作:
use Core Response Response ;
class UserController {
public function register ( Response $ response ){
$ response -> setHeader ( " x-is_register " , " true " );
return view ( " Register " );
}
}這將自動在將發送到瀏覽器的視圖響應上設置標頭。
請記住,大多數響應方法都是可以鏈接的,從而使響應實例流暢地構建。
您可以通過在響應實例上使用setContent方法設置響應的內容。
$ response -> setContent ( " ... " );但是,如果要附加響應內容,則可以通過在響應實例上使用appendContent方法來做到這一點。
$ response -> appendContent ( " ... " );您可以使用setHeader方法在響應實例上設置標頭
$ response -> setHeader ( " content-type " , " text/plain " );但是,如果您想同時設置多個標頭,則可以使用setHeaders方法並傳遞一系列標頭。
$ response -> setHeaders ([ " content-type " => " text/html " , ...]);您可以通過在響應實例上使用setHeader方法直接設置響應的狀態代碼。
$ response -> setStatusCode ( 301 );默認情況下,將為常見狀態代碼設置狀態文本。
您可以生成一個重定向響應,其中包含將用戶重定向到另一個URL所需的正確標頭,通過調用靜態方法redirect到CoreResponseResponse類。
use Core Response Response ;
Route:: get ( ' /dashboard ' , function () {
return Response:: redirect ( ' home/dashboard ' );
});但是,為簡單起見,輔助方法redirect()也可以在全球上可用,以實現相同的功能。
use Core Response Response ;
Route:: post ( ' /story/create ' , function () {
if (! $ someCondition )
return redirect ( ' /story ' , 204 );
});您還可以通過調用CoreResponseResponse類上的靜態方法JSON來生成JSON響應。傳遞給該方法的數據將轉換為適當的JSON。您還可以選擇將狀態代碼和標題數組作為第二和第三參數傳遞給該函數。
use Core Response Response ;
Route:: post ( ' /post ' , function () {
$ post = ( . . . );
return Response:: JSON ( $ post , 201 , [ " header " => " value " ]);
});中間件提供了一種方便的機制,可以檢查和過濾傳入的HTTP請求到您的應用程序。例如,您可以開發中間件來驗證應用程序用戶的身份驗證狀態。如果未對用戶進行身份驗證,則中間件將將其重定向到登錄屏幕。相反,如果對用戶進行身份驗證,則中間件將允許該請求深入應用程序。
您可以靈活地創建其他中間件,以執行超出身份驗證的各種任務。作為說明,記錄中間件可以將所有傳入的請求記錄到您的應用程序中。這些中間件組件包含在app/middlewares目錄中。
要創建新的中間件,請使用build:middleware燭台命令:
php candle build:middleware IsAuthenticated執行此命令將在app/middlewares目錄中生成一個名為“ Isauthenticated”的新鮮中間件類。在此類中,創建了一個名為handle的方法,您可以在其中闡明中間件的邏輯。
在這裡,我們僅在對用戶進行身份驗證的情況下才允許訪問該路由,否則,我們將將用戶重定向到login URI:
<?php
namespace App Middlewares ;
use App Services Auth ;
use Closure ;
use Core Request Request ;
class IsAuthenticated
{
public function handle ( Request $ request , Closure $ next )
{
if (!( /* authentication logic */ )) {
return redirect ( " /login " );
}
return $ next ( $ request );
}
}要將請求深入到該應用程序中,您應該使用$ $request $next回調。
將中間件視為HTTP請求在達到應用程序之前遍歷的“圖層”序列。每一層都可以仔細檢查請求並有可能拒絕請求。
當然,中間件可以在將請求深入到應用程序之前或之後執行任務。例如,該中間件將在請求由應用程序處理後執行其任務:
<?php
namespace App Middlewares ;
use Closure ;
use Core Request Request ;
class AfterMiddleware
{
public function handle ( Request $ request , Closure $ next )
{
$ response = $ next ( $ request );
// Perform action
return $ response ;
}
}如果您想將中間件分配給特定路由,則可以在定義路線時調用middleware方法:
Route:: get ( ' /profile ' , function () {
// ...
})-> middleware (IsAuthenticated::class);您可以通過將一系列中間件名稱傳遞給middleware方法來分配多個中間件:
Route:: get ( ' / ' , function () {
// ...
})-> middleware ([First::class, Second::class]);在定義組時,您可以通過將中間件名稱的數組傳遞到屬性middlewares的中間人名稱:
Route:: group ([ " middleware " => [HasSession::class]], function () {
Route:: get ( " / " , [StoryController::class, " index " ]);
Route:: get ( " /story/{story} " , [StoryController::class, " show " ]);
});您可以使用嵌套路線組與他們的父組相結合。在隨後的示例中,“哈斯esseess”中間件應用於"/"和"/story/{story}"路線,而“ hassession”,“ hassession”,isauth和log'中間人都將其應用於其他路線:
Route:: group ([ " middleware " => [HasSession::class]], function () {
Route:: get ( " / " , [StoryController::class, " index " ]);
Route:: get ( " /story/{story} " , [StoryController::class, " show " ]);
Route:: group ([ " middleware " => [IsAuth::class, Log::class]], function () {
Route:: get ( " /compose " , [StoryController::class, " compose " ]);
Route:: post ( " /compose " , [StoryController::class, " create " ]);
});
});在元素PHP框架中,直接從路線和控制器直接返回整個HTML文檔字符串是不切實際的。視圖提供了一種將所有HTML放入單獨文件中的方便方法。
視圖在將控制器/應用程序邏輯與演示文稿關注分開,並存儲在app/views目錄中。這些用PHP編寫的視圖文件封裝了標記。考慮一個觀點的基本示例:
<html>
<body>
<h1>Hello, <?= $ name ?> </h1>
</body>
</html>如果此視圖存儲在app/views/Welcome.php中,則可以使用路線中的全局view助手返回:
Route:: get ( ' / ' , function () {
return view ( ' Welcome ' , [ ' name ' => ' Ahmed ' ]);
});第一個參數傳遞給view助手”,對應於resources/views目錄中的視圖文件的名稱。第二個參數可以是傳遞到視圖的一系列鍵值對。例如,在上述代碼中, $name將直接訪問並包含“ ahmed”值。
也可以使用CoreViewView類上make靜態方法返回視圖:
Route:: get ( ' / ' , function () {
return View:: make ( " Post " , $ params );
});視圖可以嵌套在app/views目錄的子目錄中。 “點”表示法可用於引用嵌套視圖。例如,如果您的視圖存儲在app/views/layouts/MainLayout.php中,則可以從類似的路由/控制器返回它:
return view ( ' layouts.MainLayout ' , $ data );Elemental提供了一種方便的方法,可以在多個視圖中維護相同的佈局,從而減少代碼重複。佈局本身是一個包含佔位符{{ content }}的視圖文件。當使用佈局返回視圖時,將視圖放在佈局內容中來編譯最終視圖。
Elemental提供了一種方便的方法,可以在多個視圖中維護相同的佈局,從而減少代碼重複。佈局是一個視圖文件,它包含了指定的佔位符,用{{ content }}表示。當使用特定佈局返回視圖時,將通過將視圖的內容嵌入佈局中的指定佔位符中來實現。這種方法通過集中共同的佈局元素來簡化觀點的組織並增強代碼可維護性。
以下是一個基本示例:
<!DOCTYPE html >
< html lang =" en " >
< head >
<!-- Head content -->
</ head >
< body >
< ?= component("components.Navbar") ? >
< div style =" min-height: calc(100vh - 140px); " >
{{ content }}
</ div >
</ body >
</ html >可以以這樣的佈局返回視圖:
public function compose ()
{
return view ( " Compose " )-> withLayout ( " layouts.DashboardLayout " );
}Elemental提供了一種有力的方法來製作觀點。每個視圖本質上都是一個組件,任何視圖都可以從其他組件中組裝出來。這是一部構圖的交響曲,每件作品都有助於創造一個和諧而充滿活力的整體。
示例組件文件( views/components/Logo.php ):
<a class="logo" href="/ " >
<span class= " logo-img">
<img src="logo.png" class ="logo-text">
LOGO
</span>
</a>該組件可以在任何其他視圖文件中使用。例如,在views/Login.php中:
<div>
<?= component ( " components.Logo " ) ?>
<p>Welcome Back!</p>
<!-- Other login form elements -->
</div>因此,Elemental賦予您佈局和組件構造的能力,從而使您可以通過自上而下和自下而上的方法來構成視圖。這種靈活性使無縫的融合可以毫不費力地混合併結合元素,以製作出優雅而精緻的用戶界面。
在現代Web應用程序中,數據庫交互是一個基本方面。 Elemental旨在在各種支持的數據庫中無縫地簡化這種交互,從而利用PHP PDO的固有功能。使用Elemental,您可以靈活地使用CoreDatabaseDatabase類執行任何復雜的查詢或事務。
Elemental提供了一個強大的對象粘合映射器(ORM),該映射器(ORM)有效地抽象了許多複雜性,證明對大多數數據庫查詢是無價的。但是, CoreDatabaseDatabase可用於運行更高級的SQL查詢。
您的Elemental應用程序的所有配置都位於您應用程序的app/config/config.php配置文件中。在這裡,您可以定義所有數據庫連接,並指定默認情況下應使用哪個連接。該文件中的大多數配置選項都由應用程序環境變量的值驅動。
您的Elemental應用程序的所有配置都位於您應用程序的app/config/config.php配置文件中。在這裡,您可以定義所有數據庫連接,並指定默認情況下應使用哪個連接。該文件中的大多數配置選項都由應用程序環境變量的值驅動。
<?php
return [
" db " => [
" driver " => getenv ( " DB_DRIVER " ) ?? " mysql " ,
" host " => getenv ( " DB_HOST " ) ?? $ _SERVER [ ' SERVER_ADDR ' ],
" port " => getenv ( " DB_PORT " ) ?? " 3306 " ,
" database " => getenv ( " DB_DATABASE " ) ?? " elemental " ,
" username " => getenv ( " DB_USERNAME " ) ?? " root " ,
" password " => getenv ( " DB_PASSWORD " ) ?? "" ,
],
]; Elemental使用PDO作為基礎數據庫處理類。所有PDO功能都直接在CoreDatabaseDatabase類中可用。您可以將CoreDatabaseDatabase的實例注入任何構造函數或控制器方法來調用PDO方法。為MySQL數據庫設置了elemental的默認配置,但是您可以更改配置文件中的驅動程序。
這是通過Database實例運行查詢的一個示例:
public function tokens ( Database $ db ) {
$ user_id = 1 ;
$ sql = " SELECT * FROM access_tokens WHERE user_id = :user_id " ;
$ stmt = $ db -> prepare ( $ sql );
$ stmt -> bindValue ( " :user_id " , $ user_id );
$ stmt -> execute ();
$ tokens = $ stmt -> fetchAll ();
}有關PDO的更多信息,您可以參考PHP的PDO文檔
Elemental包括一個定制的對象鍵合映射器(ORM),它使與數據庫進行互動令人愉快。使用ORM時,每個數據庫表具有相應的“模型”,用於與該表進行交互。除了從數據庫表中檢索記錄外,模型還允許您從表中插入,更新和刪除記錄。
模型存在於app/models目錄中,並擴展了CoreModelModel類。您可以使用build:model candle命令來生成新模型。
php candle build:model Post build:model命令生成的模型將放置在app/Models目錄中。一個非常基本的模型具有以下結構:
<?php
namespace App Models ;
use Core Model Model ;
class Post extends Model
{
// ...
}表名:按照慣例,除非明確指定另一個名稱,否則“蛇案”的複數名稱將用作表名。因此,在這種情況下,Elemental將假設Post模型存儲在posts表中。
您可以通過在模型上定義tableName屬性手動指定模型的表名:
<?php
namespace App Models ;
use Core Model Model ;
class Post extends Model
{
protected $ tableName = ' elemental_posts ' ;
}主鍵:
Elemental還將假設每個模型的相應數據庫表具有一個名為id的主鍵列。如有必要,您可以在模型上定義一個受保護的$primaryKey屬性,以指定用作模型主要密鑰的不同列:
<?php
namespace App Models ;
use Core Model Model ;
class Post extends Model
{
protected $ primaryKey = ' elemental_id ' ;
}您可以將每個模型視為強大的查詢構建器,允許您流利地查詢與該模型關聯的數據庫表。
該模型的all方法將從模型關聯的數據庫表中檢索所有記錄:
use App Models Story ;
foreach (Story:: all () as $ story ) {
echo $ story [ " content " ];
}默認情況下,獲取的記錄表示為數組。但是,您可以傳遞一個模式參數,該參數控制每個記錄的表示方式。模式參數採用任何PDO提取模式。例如,
use App Models Story ;
foreach (Story:: all () as $ story ) {
echo $ story -> content ;
}allWhere方法是模型中強大的抽象,允許執行複雜的查詢。此方法採用三個參數: conditions , options和fetchMode 。
public static function allWhere( array $ conditions , array $ options = [], int $ fetchMode = PDO :: FETCH_ASSOC )條件: conditions參數是必須滿足的條款的數組才能獲取。每個條件可以是[key => value]對,也可以是[key => [operator, value]]對。
key對應於表中的特定列。[key => value]的形式,則默認運算符為=並且value是該列內的數據記錄。[key => [operator, value]] ,則可以為每個條件指定操作員。受支持的運營商是:['=', '!=', '<', '>', '<=', '>=', 'LIKE', 'IS NULL', 'IS NOT NULL'] 。選項: options參數是一個數組,該數組確定其他查詢參數,例如order by , limit等。選項參數中支持的構造包括:
"orderBy""limit""offset""sortDir" fetchmode: fetchMode參數控制每個獲取的記錄的表示方式。模式參數採用任何PDO提取模式:
PDO::FETCH_ASSOCPDO::FETCH_NUMPDO::FETCH_BOTHPDO::FETCH_OBJPDO::FETCH_CLASSPDO::FETCH_INTOPDO::FETCH_LAZYPDO::FETCH_KEY_PAIR一個例子將使情況更加清楚:
use Core Request Request ;
class StoryController {
const PAGE_SIZE = 10 ;
public function index ( Request $ request )
{
$ search = $ request -> search ;
$ categoryId = $ request -> category_id ;
$ sortBy = $ request -> sort_by ; // ASC or DESC, Default = ASC
$ page = $ request -> page ;
$ orderBy = $ request -> order_by ;
return Story:: allWhere (
[
" category_id " => $ categoryId ,
" title " => [ " LIKE " , " % $ search $ " ],
],
[
" limit " => static :: PAGE_SIZE ,
" orderBy " => $ orderBy ,
" sortDir " => $ sortBy ,
" offset " => ( $ page - 1 ) * static :: PAGE_SIZE ,
],
PDO :: FETCH_OBJ
);
}
}除了檢索與給定查詢相匹配的所有記錄外,您還可以使用find和where檢索單個記錄。這些方法沒有返回一系列記錄,而是返回單個模型實例:
查找:這將獲取與表的主鍵匹配的第一個記錄。
$ flight = Story:: find ( 1 );地點:其中的方法採取了一系列條件,這些條件必須滿足以獲取的記錄。每個條件可以是[key => value]對,也可以是[key => [operator, value]]對。
key對應於表中的特定列。[key => value]的形式,則默認運算符為=並且value是該列內的數據記錄。[key => [operator, value]] ,則可以為每個條件指定操作員。受支持的運營商是:['=', '!=', '<', '>', '<=', '>=', 'LIKE', 'IS NULL', 'IS NOT NULL'] 。例如
$ user = User:: where ([ " email " => $ email ]);
$ liked = Like:: where ([ " user_id " => $ user -> id , " story_id " => $ story_id ]);要將新記錄插入數據庫中,您可以實例化新的模型實例並在模型上設置屬性。然後,在模型實例上調用save方法:
<?php
namespace App Controllers ;
use App Models Story ;
use Core Request Request ;
class StoryController
{
public function store ( Request $ request )
{
$ story = new Story ;
$ story -> name = $ request -> name ;
$ story -> save ();
return redirect ( ' /story ' );
}
}在此示例中,我們將來自傳入HTTP請求的name字段分配給AppModelsStory Model實例的name屬性。當我們調用save方法時,記錄將插入數據庫中。當調用save方法時,將自動設置模型created_at的時間戳,因此無需手動設置它。
另外,您可以使用靜態create方法使用單個PHP語句“保存”新模型。插入的模型實例將通過create方法返回給您:
use App Models Story ;
$ story = Story:: create ([
' name ' => ' A tale of elemental magic ' ,
]);save方法還可以用於更新數據庫中已經存在的模型。要更新模型,您應該檢索並設置要更新的任何屬性。然後,您應該調用模型的save方法。
use App Models Story ;
$ story = Story:: find ( 10 );
$ story -> name = ' An elemental tale of magic ' ;
$ story -> save ();另外,您可以使用靜態update方法更新模型實例。第一個參數是模型的ID,第二個參數需要是列值對的數組。
use App Models Story ;
$ story = Story:: update ( 10 , [ " name " => " A tale " , " content " => " Once upon a time .... " ]);要刪除模型,您可以在模型實例上調用destroy方法:
use App Models Story ;
$ story = Story:: find ( 12 );
$ story -> destroy ();但是,如果您知道模型的主要鍵,則可以刪除模型,而無需通過調用delete方法明確檢索模型。返回已刪除的記錄的id 。
use App Models Story ;
Story:: delete ( 12 );您可以在模型上調用data方法,以以數組形式檢索模式實例的所有屬性。
$ user = User:: find ( 10 );
$ user_data = $ user -> data ();蠟燭是元素的命令線引擎。蠟燭作為candle腳本存在於您的應用程序的根源,並提供了許多有用的命令,旨在幫助您進行應用程序的開發過程。要查看所有可用蠟燭命令的列表,您可以使用help命令:
php candle help這還將顯示您可能創建的自定義命令。
到現在為止,您一定已經ignited元素的蠟燭來運行您的應用程序。此ignite命令在IP地址127.0.0.1上為該應用提供服務,搜索從8000起的自由端口。如果占據端口8000,Elemental會自動嘗試綁定到下一個可用端口(例如,8001)等。
php candle ignite您可以靈活地根據您的要求自定義服務器設置。
自定義主機使用--host參數指定特定的IP地址。例如:
php candle ingite --host=192.168.1.10自定義端口如果您喜歡綁定到特定端口,請使用--port參數:
php candle ingite --port=8080要同時使用自定義IP和端口服務您的應用程序,請同時提供--host和--port參數:
php candle ingite --host=192.168.1.10 --port=8080可以按任何順序進行--host和--port論點。
要獲取應用程序中所有註冊路線的全面視圖,請使用route:list命令:
bash
php candle route:list您可以使用Candle build命令為您的模型,控制器,中間件和命令生成文件。
要創建模型,請執行以下命令:
php candle build:model Story此命令將在appmodels Directory中生成一個名為Story.php的文件,其中包含Story類。
對於生成控制器, build命令類似地使用:
php candle build:controller StoryController執行此命令將在appcontrollers目錄中生成一個名為StoryController.php的文件,其中包含MyController類。
要生成中間件,請使用build命令如下:
php candle build:middleware HasSession這將在appmiddleware目錄中創建一個名為HasSession.php的文件,並包含handle方法。
對於命令生成,用適當的參數執行build命令:
php candle build:command Migration執行此命令將在appcommands目錄中生成一個名為Migration.php的文件,其中包含Migration類和handle方法。
生成自定義命令是可以體驗蠟燭的力量的地方。命令存儲在app/commands目錄中,至關重要的是將它們加載到appcommandsCommands.php中返回的數組中,以在應用程序中正確註冊。
生成命令後,為類的key和description屬性定義值。 key用作命令的參數,而description將顯示在幫助屏幕中。執行命令時,將調用handle方法,您可以將命令邏輯放在此方法中。
您可以鍵入命令處理所需的任何依賴項。 Elemental的DI容器將自動注入handle方法簽名中的所有依賴類型。
讓我們看一個示例命令:
<?php
namespace App Commands ;
use App Models User ;
use App Service MailService ;
use Core Console Command ;
class SendEmails extends Command
{
protected $ key = ' mail:send ' ;
protected $ description = ' Send mails to all users ' ;
public function handle ( MailService $ mailService ): void
{
$ mailService -> send (User:: all ());
}
}在命令行中執行命令:
php candle mail:send您可以使用Elemental的CoreConsoleCommander檢索通過命令行傳遞的任何輸入。 CoreConsoleCommander提供了一種名為getArgs的方法,該方法返回從命令行傳遞的一系列輸入。指揮官實例可以通過處理程序方法進行類型並根據需要使用。
一個具體的例子將清楚:
<?php
namespace App Commands ;
use Core Console Command ;
use Core Console Commander ;
class Migration extends Command
{
protected $ key = " migrate " ;
protected $ description = " Custom migration handler. " ;
private $ commander ;
public function handle ( Commander $ commander , Database $ db )
{
$ args = $ commander -> getArgs ();
if (! isset ( $ args [ 1 ])) {
$ this -> up ();
return ;
}
switch ( $ args [ 1 ]) {
case " fresh " :
$ this -> downThenUp ();
break ;
case " delete " :
$ this -> down ();
break ;
default :
$ this -> up ();
}
}
public function up ()
{
$ sql = " CREATE TABLE IF NOT EXISTS users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
bio TEXT,
image VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) " ;
try {
$ db -> exec ( $ sql );
console_log ( " Table 'users' created successfully! " );
} catch ( PDOException $ e ) {
console_log ( " Table creation error: " . $ e -> getMessage ());
}
}
public function down ()
{
$ sql = " DROP TABLE IF EXISTS users " ;
try {
$ db -> exec ( $ sql );
console_log ( " Table 'users' deleted successfully! " );
} catch ( PDOException $ e ) {
console_log ( " Table deletion error: " . $ e -> getMessage ());
}
}
public function downThenUp ()
{
$ this -> down ();
$ this -> up ();
}
}建議與命令類的構造函數內部的構成濃度依賴性鍵入。
在命令行中執行這些遷移命令:
php candle migrate
php candle migrate fresh
php candle migrate delete如您所見,生成命令非常強大,可以幫助實現各種功能。在這裡,已經建立了一個自定義遷移處理程序。您可以擴展和組織上述結構,或創建可以處理遷移邏輯的自定義遷移服務。
命令也可以用於處理任務調度。您可以創建一個執行某些邏輯的命令,然後將命令傳遞給操作系統CRON處理程序。
元素包括各種全局“助手” PHP功能。您可以以任何方便的方式使用這些功能。
app功能返回Application實例:
$ app = app ();當您要註冊自己的服務並解決任何框架或自定義服務時,這非常有用。
app ()-> bind (CustomService::class, function () {
return new CustomService ( new anotherService ());
});
$ service = app ()- make (CustomService::class); dump功能將變量傳遞給第一個參數。您還可以傳遞一個可以用作屏幕上標識符的其他第二個參數:
dump ( $ value );
dump ( $ user , " user " ); dd函數轉儲給定的變量並結束腳本的執行:
dd ( $ value );
dd ( $ user , " user " ); console_log函數是與dump函數不同的記錄變量的獨特工具。值得注意的是,它不會將輸出返回到瀏覽器;相反,它將信息引導到腳本啟動的控制台。您可以將任何可變數量的參數傳遞給console_log函數。
console_log ( $ value );
console_log ( $ user , $ post , $ image , $ comment ); router函數返回返回Router實例。
view函數用於從控制器方法返回視圖:
return view ( ' Login ' ); component函數用於返回視圖作為要在另一個視圖中使用的組件:
<body>
<?= component ( " Logo " ) ?>
//...
</body> redirect函數返回重定向HTTP響應,並用於重定向到其他任何路線。
return redirect ( ' /home ' );Elemental提供了一種方便的方法來處理應用程序拋出的所有異常。
AppExceptionsHandler類的handle方法是您的應用程序拋出的所有異常,然後再呈現給用戶。默認情況下,應用程序拋出的異常將格式化,並將將結構化響應發送回瀏覽器。但是,在手柄方法內,您可以攔截任何異常,並在響應發送迴響應之前執行自定義邏輯。
您甚至可以發送自定義視圖或響應。
<?php
namespace App Exceptions ;
use Core Exception ExceptionHandler ;
class Handler extends ExceptionHandler
{
public function handle ( $ e )
{
// Perform some processing here
// You can customize the handling of exceptions based on your requirements
}
}Elemental默認情況下定義了一些特定的異常類:
AppExceptionModelNotFoundExceptionRouteNotFoundExceptionRouterExceptionViewNotFoundException如果您需要以不同的方式處理不同類型的異常,則可以相應地修改handle方法:
<?php
class Handler extends ExceptionHandler
{
public function handle ( $ e )
{
if ( $ e instanceof ModelNotFoundException || $ e instanceof RouteNotFoundException) {
return view ( " 404 " )-> withLayout ( " layouts.DashboardLayout " );
}
if ( $ e instanceof ViewNotFoundException) {
return view ( " Home " );
}
// Handle other specific exceptions as needed
}
}您可以自由從基本Exception類延伸來創建自己的異常類,然後可以根據需要處理。
請根據應用程序的特定需求自定義handle方法。
該應用程序的所有配置設置都集中在appconfigconfig.php文件中。這些配置涵蓋了各個方面,例如數據庫連接信息和應用程序必不可少的其他核心設置。
為了滿足應用程序可能運行的不同環境,在根目錄中提供了.env.example文件。該文件概述了可以配置的通用環境變量。如果您在團隊中工作,建議將.env.example文件與占位符值一起。這使其他開發人員清楚地清楚運行應用程序需要哪些環境變量。
當您的應用程序收到請求時, .env文件中列出的所有變量都將加載到$_ENV PHP Super-Global中。然後,您可以使用getenv函數從配置文件中的這些變量中檢索值。
$ appName = getenv ( " APP_NAME " );要訪問配置值,您可以使用類型礦體,並將CoreConfigConfig類註入構造函數,控制器方法或路由關閉。
use Core Config Config ;
class YourClass {
public function __construct ( Config $ config ) {
$ driver = $ config -> db [ " driver " ];
$ host = $ config -> db [ " host " ];
$ port = $ config -> db [ " port " ];
}
// Your other methods or code here
}通過執行此操作,您有一種干淨而有組織的方法來檢索應用程序中的配置值。
這種方法可以使您的配置集中,並允許根據環境輕鬆更改。它還促進了乾淨可維護的代碼庫。
Elemental引入了一個受Laravel啟發的外牆系統,為應用程序依賴注入(DI)容器中的類提供了方便且表現力的靜態接口。外牆充當服務容器中類的靜態代理,在簡潔的語法和傳統靜態方法的可檢驗性和靈活性之間提供平衡。
在Elemental中, CoreFacadeRoute用作立面,為應用程序的路由器實例提供靜態接口,使您能夠在routes.php文件中使用它:
// routes.php
<?php
use Core Facade Route ;
Route:: get ( " /register " , [AuthController::class, " showRegister " ]);
Route:: get ( " /login " , [AuthController::class, " showLogin " ]);
Route:: get ( " /logout " , [AuthController::class, " logout " ]);
Route:: post ( " /register " , [AuthController::class, " register " ]);要為任何班級創建自定義外牆,請按照以下步驟:
CoreFacadeFacade類的FacadeClass 。getFacadeAccessor的靜態方法,並在DI容器中返回了關聯實例的類字符串。這是創建PaymentGateway立面的示例:
<?php
use Core Facade Facade ;
use Core Services PaymentGateway ;
class PaymentGatewayFacade extends Facade
{
protected static function getFacadeAccessor ()
{
return PaymentGateway::class;
}
}現在,您可以通過在相應的FacadeClass上調用靜態方法來訪問自定義類的實例方法。
拉拉維爾是魔術。像任何毫無戒心的麻瓜一樣,它的附魔也會嚇到您。直到有一天,您敢於拿起魔杖並開始揮舞魔杖。然後,您愛上了它。
基本框架是根據MIT許可證許可的開源軟件。
歡迎所有貢獻。請先為任何功能請求或錯誤創建問題。然後,訂購存儲庫,創建一個分支並進行任何更改以修復錯誤或添加功能並創建拉動請求。就是這樣!謝謝!
對於錯誤報告,功能請求或一般問題,請使用問題跟踪器。