يوصى بشدة باستخدام Flightphp/active-record بدلاً من هذه المكتبة. لن يتم إجراء أي تطوير إضافي لهذه المكتبة، ولكنها تعمل كما هي الآن.
super model عبارة عن فئة php من نوع ORM بسيطة جدًا للتفاعل بسهولة مع الجداول في قاعدة البيانات دون كتابة الكثير من تعليمات SQL البرمجية في كل مكان.
ولإثبات ذلك، إليك سطور التعليمات البرمجية...
$ cloc src/
1 text file.
1 unique file.
0 files ignored.
github.com/AlDanial/cloc v 1.74 T=0.01 s (71.8 files/s, 48768.5 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
PHP 1 86 246 347
-------------------------------------------------------------------------------
تمت كتابة هذا مع وضع الأداء في الاعتبار. لذلك، على الرغم من أنها لن تلبي كل المتطلبات في كل مشروع تم إنشاؤه على الإطلاق، إلا أنها ستعمل في معظم الحالات، وستقوم بعمل رائع أيضًا!
من السهل البدء باستخدام super model ، ما عليك سوى توسيع فئة super model وتحديد اسم الجدول. هذا كل ما في الأمر.
<?php
use n0nag0n Super_Model ;
class User extends Super_Model {
protected $ table = ' users ' ;
}
والآن ماذا عن بعض الأمثلة البسيطة عن كيفية عملها؟
أولاً لنفترض الجدول التالي:
Table: users
---------------------------------------------------------
| id | email | company_id |
| 1 | [email protected] | 50 |
| 2 | [email protected] | 61 |
| 3 | [email protected] | 61 |
---------------------------------------------------------
<?php
// somefile.php
$ pdo = new PDO ( ' sqlite::memory: ' , '' , '' , [ PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ]);
$ User = new User ( $ pdo );
// WHERE company_id = 50
$ users = $ User -> getAllBycompany_id ( 50 );
// same as above
$ users = $ User -> getAll ([ ' company_id ' => 50 ]);
سهل للغاية، معصور ليمون، أليس كذلك؟
getBy*(mixed $value): array [result]
هذه هي الطريقة التي تقوم بإرجاع صف واحد من القيمة المحددة. يشير الجزء *
من الطريقة إلى حقل في قاعدة البيانات. اسم الحقل حساس لحالة الأحرف لأي اسم حقل موجود في جدول قاعدة البيانات.
// get by the id field on the users table
$ User -> getByid ( 3 );
/*
[
'id' => 3,
'email' => '[email protected]',
'company_id' => 61
]
*/
$ User -> getBycompany_id ( 61 );
/*
// it only will pull the first row, not all rows
[
'id' => 2,
'email' => '[email protected]',
'company_id' => 61
]
*/
getAllBy*(mixed $value): array [ [result], [result] ]
هذا هو مرشح الاختصار لإرجاع كافة الصفوف بقيمة معينة. يشير الجزء *
من الطريقة إلى حقل في قاعدة البيانات. اسم الحقل حساس لحالة الأحرف لأي اسم حقل موجود في جدول قاعدة البيانات.
// this is pointless, but will still work
$ User -> getAllByid ( 3 );
/*
[
[
'id' => 3,
'email' => '[email protected]',
'company_id' => 61
]
]
*/
$ User -> getAllBycompany_id ( 61 );
/*
[
[
'id' => 2,
'email' => '[email protected]',
'company_id' => 61
],
[
'id' => 3,
'email' => '[email protected]',
'company_id' => 61
]
]
*/
getAll(array $filters, bool $return_one_row = false): array [ [result], [result] ] or [result]
هذا هو الفلتر الذي يمكنك من خلاله إضافة مجموعة من التخصيصات لتصفية البيانات من الجدول الخاص بك. هناك بعض المفاتيح الفريدة التي يجب أن تكون على دراية بها وبعض عوامل التشغيل لمساعدتك في سحب بياناتك المحددة.
// Full example
$ filters = [
//
// arguments in the WHERE statement
//
' some_field ' => 5 , // some_field = ?
' some_field-= ' => 5 , // some_field = ?
' another_field ' => ' IS NULL ' , // some_field IS NULL
' another_field ' => ' IS NOT NULL ' , // some_field IS NOT NULL
' another_field-> ' => ' Apple ' , // another_field > ?
' another_field->= ' => ' Apple ' , // another_field >= ?
' another_field-< ' => ' Apple ' , // another_field < ?
' another_field-<= ' => ' Apple ' , // another_field <= ?
' another_field-!= ' => ' Apple ' , // another_field != ?
' another_field-<> ' => ' Apple ' , // another_field <> ?
' another_field-LIKE ' => ' Ap%ple ' , // another_field LIKE ?
' another_field-NOT LIKE ' => ' Apple% ' , // another_field NOT LIKE ?
' another_field-IN ' => [ ' Apple ' , ' Banana ' , ' Peach ' ], // another_field IN(??) double question mark gets parsed as array
' another_field-NOT IN ' => [ ' Apple ' , ' Banana ' , ' Peach ' ], // another_field NOT IN(??) double question mark gets parsed as array
// If you need some custom action
' another_field-RAW-> DATE_SUB(?, INTERVAL 1 DAY) ' => ' 1980-01-01 ' , // another_field > DATE_SUB(?, INTERVAL 1 DAY)
//
// Other parts of the query
//
// choose what columns you want to select
' select_fields ' => ' id, first_name ' ,
// Get any joins
' joins ' => [ ' LEFT JOIN companies ON companies.id = users.company_id ' ],
// Group by
' group_by ' => ' company_id ' ,
// having
' having ' => ' count > 5 ' ,
// order by
' order_by ' => ' id DESC ' ,
// limit
' limit ' => 15 ,
// offset
' offset ' => 10000 ,
];
$ users = $ User -> getAll ( $ filters );
هناك أيضًا بعض خيارات التكوين الأساسية مع خصائص النموذج.
إذا كان لديك نموذج تعرف أنه سيُرجع دائمًا مجموعة نتائج صغيرة وتريد أن تكون قادرًا على الاستعلام عن الجدول بأكمله، فقم بتعيين هذه الخاصية. وإلا فهي حماية بحيث إذا لم يتم توفير معلمات SQL، فلن تتمكن من استرداد مجموعة النتائج بأكملها (مما قد يؤدي إلى تعطل وحرق العديد من الأشياء).
use n0nag0n Super_Model ;
class User extends Super_Model {
protected $ table = ' users ' ;
protected $ disallow_wide_open_queries = false ;
}
create(array $data): int [insert id]
سيؤدي هذا إلى إنشاء صف واحد في الجدول، ولكن إذا قمت بتوفير مصفوفة متعددة الأبعاد، فسيتم إدراج صفوف متعددة. من المفترض وجود مفتاح أساسي id
.
$ User -> create ([ ' email ' => ' [email protected] ' , ' company_id ' => 55 ]);
// returns 4
$ User -> create ([ [ ' email ' => ' [email protected] ' , ' company_id ' => 55 ], [ ' email ' => ' [email protected] ' , ' company_id ' => 56 ] ]);
// returns 6, only the last id will be returned
update(array $data, string $update_field = 'id'): int (number of rows updated)
سيؤدي هذا إلى إنشاء صف واحد في الجدول، ولكن إذا قمت بتوفير مصفوفة متعددة الأبعاد، فسيتم إدراج صفوف متعددة. من المفترض وجود مفتاح أساسي id
.
$ User -> update ([ ' id ' => 1 , ' email ' => ' [email protected] ' ]);
// returns 1 and will only update the email field
$ User -> update ([ ' email ' => ' [email protected] ' , ' company_id ' => 61 ], ' email ' );
// returns 1
$ User -> update ([ ' company_id ' => 61 , ' email ' => ' [email protected] ' ], ' company_id ' );
// returns 3, not really logical, but it would update all the emails
ماذا لو كنت تريد طريقة آلية لتغيير نتيجتك إذا تم إطلاق علامة معينة؟ سهل سهل. هناك طريقة تسمى processResult()
والتي سيتم تشغيلها من خلال كل نتيجة تقوم بسحبها. يمكنك إدخال مرشحات خاصة لهذه الطريقة في المفتاح $filters['processResults']
.
<?php
use n0nag0n Super_Model ;
class User extends Super_Model {
protected $ table = ' users ' ;
public processResult (array $ process_filters , array $ result ): array {
// add some trigger here and do whatever checks you need
if( isset ( $ process_filters ['set_full_name']) && $ process_filters ['set_full_name'] === true && !empty( $ result ['first_name']) && !empty( $ result ['last_name'])) {
$ result ['full_name'] = $ result [ ' first_name ' ]. ' ' . $ result [ ' last_name ' ];
}
return $ result ;
}
}
// later on in some other file.
$ User = new User ( $ pdo );
// setting the processResults filter here is the key to connecting the getAll statement with your processResult method
$ users = $ User -> getAll ([ ' company_id ' => 51 , ' processResults ' => [ ' set_full_name ' => true ] ]);
echo $ users [ 0 ][ ' full_name ' ]; // Bob Smith
ماذا لو كنت بحاجة إلى إجراء استعلام SQL معقد ومجنون لا يقع ضمن نطاق هذه الفئة أو مرشحات getAll()
؟
تذكر أن الهدف من هذه الدورة ليس تلبية كل المتطلبات من كل مشروع قائم أو سيكون موجودًا على الإطلاق، ولكنه سيوصلك إلى 90% من الطريق. في ضوء ذلك، هناك طريقة بسيطة لتنفيذ السؤال أعلاه. ما عليك سوى استخدام RAW SQL لمرة واحدة.
<?php
use n0nag0n Super_Model ;
class User extends Super_Model {
protected $ table = ' users ' ;
public function processCrazyKukooQuery ( /* add whatever required fields you need */ ): array {
$ db = $ this -> getDbConnection ();
// shamelessly ripped from StackOverflow
$ statement = $ db -> prepare ( " SELECT
DISTINCT
t.id,
t.tag,
c.title AS Category
FROM
tags2Articles t2a
INNER JOIN tags t ON t.id = t2a.idTag
INNER JOIN categories c ON t.tagCategory = c.id
INNER JOIN (
SELECT
a.id
FROM
articles AS a
JOIN tags2articles AS ta ON a.id=ta.idArticle
JOIN tags AS tsub ON ta.idTag=tsub.id
WHERE
tsub.id IN (12,13,16)
GROUP BY a.id
HAVING COUNT(DISTINCT tsub.id)=3
) asub ON t2a.idArticle = asub.id " );
$ statement -> execute ();
return $ statement -> fetchAll ();
}
}
ما عليك سوى تشغيل composer test
لتشغيل phpunit
و phpstan
. حاليًا بتغطية 100% وهذا هو المكان الذي أرغب في الاحتفاظ به.
ملاحظة حول التغطية بنسبة 100%: على الرغم من أن الكود قد يحتوي على تغطية بنسبة 100%، إلا أن التغطية الفعلية مختلفة. الهدف هو اختبار العديد من السيناريوهات المختلفة مقابل الكود للتفكير في الكود وتوقع النتائج غير المتوقعة. أقوم بتشفير التغطية "الحقيقية"، وليس تغطية "تم تشغيل الكود".