اذهب إلى المحتوى

استخدام MongoDB و Redis في PHP


سارة محمد2

كل شيء بين MongoDB و PHP

المتطلبات

  • خادم MongoDB يعمل على منفذ والذي هو 27017 عادةً. (اكتب mongod في موجه الأوامر لتشغيل خادم mongod)
  • لغة php مثبّتة إما باستخدام cgi أو fpm مع إضافة MongoDB (إضافة MongoDB لا توجد مع php بشكلٍ افتراضي).
  • مكتبة المُنشئ (Composer) (‏mongodb/mongodb). نفّذ التعليمة php composer.phar require "mongodb/mongodb=^1.0.0"‎ في المجلد الجذر للمشروع لتثبيت مكتبة MongoDB).

إذا تأكدت من المتطلبات يمكنك بعدها الانتقال للخطوة التالية:

  • التحقق من تثبيت php: نفّذ التعليمة php -v في موجه الأوامر إذا لم تكن متأكدًا من أنّها مثبّتة لديك وستُرجع مايشبه التالي:
PHP 7.0.6 (cli) (built: Apr 28 2016 14:12:14) ( ZTS ) Copyright (c) 1997-2016 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
  • التحقق من تثبيت MongoDB: وذلك بتنفيذ التعليمة mongo --version والتي ستُرجع MongoDB shell version: 3.2.6.
  • التحقق من تثبيت المُنشئ: وذلك بتنفيذ التعليمة php composer.phar --version التي ستُرجع Composer version 1.2-dev (3d09c17b489cd29a0c0b3b11e731987e7097797d) 2016-08-30 16:12:39.

الاتصال من php إلى MongoDB

<?php
// ‫يجب أن يشير هذا المسار إلى المحمِّل التلقائي للمُنشئ من المكان حيث يجب أن تُحمَّل مكتبة MongoDB 
 require 'vendor/autoload.php';

// استخدام اسم مستخدم وكلمة مرور مخصَّصَين
try { 
    $mongo = new MongoDB\Client('mongodb://username:password@localhost:27017');
    print_r($mongo->listDatabases()); 
} 
catch (Exception $e) { 
    echo $e->getMessage(); 
}

// استخدام إعدادات افتراضية
try { 
    $mongo = new MongoDB\Client('mongodb://localhost:27017');
    print_r($mongo->listDatabases()); 
} catch (Exception $e) { 
    echo $e->getMessage(); 
} 

ستتصل الشيفرة السابقة باستخدام مكتبة المُنشئ MongoDB (‏mongodb/mongodb) المضمّنة بالشكل vendor/autoload.php للاتصال بخادم MongoDB? الذي يعمل على المنفذ 27017. ستتصل إذا كان كل شيء صحيحًا وتعرض قائمة مصفوفة أما إذا حدث استثناء بالاتصال إلى خادم MongoDB? ستُطبع رسالة.

الإنشاء (الإضافة) في MongoDB

يستخدم MongoDB المجموعة (collection) بدلًا من الجداول كما هو الحال في SQL.

نستخدم في الشيفرة التالية الكائن ‎$mongo لاختيار قاعدة البيانات والمجموعة، إذا لم توجد قاعدة البيانات (demo في مثالنا) والمجموعة (beers في مثالنا) سينشئهما MongoDB تلقائيًا.

ونستخدم ‎$collection لإضافة ملف في MongoDB، يشبه الملف السطر في SQL، وكل ملف مُنشئ له معرِّف فريد.

<?php
$collection = $mongo->demo->beers;

$result = $collection->insertOne( [ 'name' => 'Hinterland', 'brewery' => 'BrewDog' ] );

echo "Inserted with Object ID '{$result->getInsertedId()}'"; 

استخدمنا في المثال الكائن ‎$mongo المستخدم سابقًا في شيفرة الاتصال من php إلى MongoDB، يستخدم MongoDB نوع تنسيق البيانات JSON لذا سنستخدم في php المصفوفة لإضافة بيانات إلى MongoDB، ستحوّل مكتبة mongo من المصفوفة إلى JSON وبالعكس، لكل ملف في MongoDB معرّف خاص به يمكننا الحصول عليه عند الإضافة باستخدام الشيفرة:

 $result->getInsertedId();

القراءة (البحث) في MongoDB

نستخدم التابع find()‎ للاستعلام عن السجلات، ويكون المعامل مصفوفة تتضمن زوج قيمة مفتاح نريد إيجاده، تُرجَع النتيجة على شكل مصفوفة ونستخدم foreach لفلترة المفاتيح المطلوبة.

$result = $collection->find( [ 'name' => 'Hinterland', 'brewery' => 'BrewDog' ] );

foreach ($result as $entry) { 
    echo $entry['_id'], ': ', $entry['name'], "\n"; 
} 
?>

مثال آخر للبحث عن عدة مستخدمين اسمهم "Mike":

$filter = ['name' => 'Mike'];
$query = new \MongoDB\Driver\Query($filter);

$cursor = $manager->executeQuery('database_name.collection_name', $query); 
foreach ($cursor as $doc) { 
    var_dump($doc); 
}

ونستخدم التابع findOne()‎ للحصول على ملف واحد، مثال للبحث عن مستخدم واحد فقط له معرِّف محدد:

$options = ['limit' => 1]; 
$filter = ['_id' => new \MongoDB\BSON\ObjectID('578ff7c3648c940e008b457a')]; 
$query = new \MongoDB\Driver\Query($filter, $options); 

$cursor = $manager->executeQuery('database_name.collection_name', $query); 
$cursorArray = $cursor->toArray(); 
if(isset($cursorArray[0])) { 
    var_dump($cursorArray[0]); 
}

الحذف في MongoDB

تعيد الشيفرة التالية 1 في حال تم الحذف بنجاح وتعيد 0 في حال الفشل.

<?php
$result = $collection->drop( [ 'name' => 'Hinterland'] ); 
print_r($result->ok); 
?>

يمكنك الاطلاع في التوثيق الرسمي من MongoDB على العديد من التوابع التي يمكن تطبيقها على ‎$collection.

الاتصال مع MongoDB وإجراء العمليات

إنشاء اتصال MongoDB يمكنك استخدامه للاستعلام منه لاحقًا:

$manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017');

ستتعلم في المثال التالي كيفية الاستعلام باستخدام كائن الاتصال، تُغلق هذه الإضافة الاتصال بشكلٍ تلقائي لذا ليس من الضروري إغلاقه يدويًا.

إضافة ملف

مثال لإضافة ملف:

$document = [ 
    'name' => 'John', 
    'active' => true, 
    'info' => ['genre' => 'male', 'age' => 30] 
]; 
$bulk = new \MongoDB\Driver\BulkWrite; 
$_id1 = $bulk->insert($document); 
$result = $manager->executeBulkWrite('database_name.collection_name', $bulk);

تحديث ملف

مثال لتحديث كل الملفات التي اسمها "John":

$filter = ['name' => 'John']; 
$document = ['name' => 'Mike']; 

$bulk = new \MongoDB\Driver\BulkWrite; 
$bulk->update( 
    $filter, 
    $document, 
    ['multi' => true] 
); 
$result = $manager->executeBulkWrite('database_name.collection_name', $bulk);

حذف ملف

مثال لحذف كل الملفات التي اسمها "Peter":

$bulk = new \MongoDB\Driver\BulkWrite; 

$filter = ['name' => 'Peter']; 
$bulk->delete($filter); 

$result = $manager->executeBulkWrite('database_name.collection_name', $bulk);

استخدام Redis مع PHP

الاتصال إلى كائن Redis

بفرض أنّ الخادم الافتراضي يعمل على المضيف المحلي مع المنفذ الافتراضي?، عندها يكون أمر الاتصال إلى خادم Redis هذا:

$redis = new Redis(); 
$redis->connect('127.0.0.1', 6379);

تثبيت PHP Redis على نظام التشغيل أبونتو

نثبّت أولًا خادم Redis:

sudo apt install redis-server 

ثم نثبّت وحدة PHP:

sudo apt install php-redis

ثم نعيد تشغيل خادم Apache:

sudo service apache2 restart

تنفيذ أوامر Redis في PHP

توفر وحدة Redis PHP الوصول إلى نفس الأوامر كعميل Redis CLI لذا يمكن استخدامها بشكلٍ مباشر.܏ الصياغة كالتالي:

// إنشاء مفتاحين جديدين
$redis->set('mykey-1', 123); 
$redis->set('mykey-2', 'abcd');

// الحصول على مفتاح واحد
// 123

// ‫الحصول على كل المفاتيح التي بدايتها 'my-key-‎‎'
var_dump($redis->keys('mykey-*'));
// '123', 'abcd'

الإضافة PDO في PHP

تسمح الإضافة PDO للمطورين بالاتصال بأنواع مختلفة كثيرة من قواعد البيانات وتنفيذ استعلامات مختلفة عليها بطريقة موحدة كائنية التوجه.

منع حقن SQL باستخدام استعلامات ذات وسائط

حقن SQL هو هجوم يسمح لمستخدم ضار بتعديل استعلام SQL بإضافة أوامر غير مرغوب بها إلى الاستعلام، مثلًا الاستعلام التالي معرَّض للخطر:

$sql = 'SELECT name, email, user_level FROM users WHERE userID = ' . $_GET['user'];
$conn->query($sql);

تسمح هذه الشيفرة لأي مستخدم بالتعديل في قاعدة البيانات بشكلٍ أساسي كما يرغب، مثلًا بفرض لدينا سلسلة الاستعلام التالية:

page.php?user=0;%20TRUNCATE%20TABLE%20users;

تجعل هذه السلسلة الاستعلام يكون بالشكل:

SELECT name, email, user_level FROM users WHERE userID = 0; TRUNCATE TABLE users;

يعدّ هذا المثال مبالغًا فيه (معظم هجمات حقن SQL لا تهدف إلى حذف البيانات ولا تدعم معظم دوال تنفيذ استعلام PHP الاستعلام المتعدد)، هذا مثال على إمكانية جعل هجوم حقن SQL ممكنًا من خلال التجميع غير المتقن للاستعلام، لسوء الحظ فإنَّ مثل هذه الهجمات شائع جدًا وفعال بشكلٍ كبير بسبب المبرمجين الذين يفشلون في اتخاذ الاحتياطات المناسبة لحماية بياناتهم.

الحل المناسب لمنع حدوث حقن SQL هو تعليمات التحضير (prepared statements)، فنستخدم عنصرًا بديلًا (placeholder) بدلًا من دمج بيانات المستخدم بشكلٍ مباشر مع الاستعلام، ثم تُرسَل البيانات بشكلٍ منفصل وبالتالي لا توجد فرصة لمحرك SQL أن يحتار بخصوص بيانات المستخدم. ولاحظ أنّ إضافة PHP MySQLi تدعم أيضًا تعليمات التحضير.

تدعم PDO نوعين من العناصر البديلة (لا يمكن استخدام العناصر البديلة لتسمية الأعمدة أو الجداول وإنما فقط للقيم):

1- العناصر البديلة المسماة، نقطتان (:) يتبعهما اسم واضح مثل (‎:user).

// استخدام العناصر البديلة المسماة
$sql = 'SELECT name, email, user_level FROM users WHERE userID = :user';
$prep = $conn->prepare($sql);

// مصفوفة ترابطية
$prep->execute(['user' => $_GET['user']]); 
$result = $prep->fetchAll();

2- العناصر البديلة الموضعية التقليدية في SQL تُمثَّل بالعلامة ?.

// استخدام العناصر البديلة الممثَّلة بعلامة الاستفهام
$sql = 'SELECT name, user_level FROM users WHERE userID = ? AND user_level = ?';
$prep = $conn->prepare($sql);

// مصفوفة مفهرسة
$prep->execute([$_GET['user'], $_GET['user_level']]);
$result = $prep->fetchAll();

يجب أن تعلم أنّه إذا احتجت إلى تغيير اسم عمود أو جدول بشكلٍ ديناميكي فإنّ هذه ممارسة سيئة وعلى مسؤوليتك الأمنية الخاصة، مع أنّه يمكنك القيام بذلك عن طريق دمج السلاسل النصية. إحدى الطرق لتحسين أمان مثل هذه الاستعلامات هي تعيين جدول فيه القيم المسموح بها ومقارنة القيمة التي تريدها للدمج مع هذا الجدول.

يجب أن تعلم أنّه من المهم ضبط ترميز محارف الاتصال ليكون DSN (‏Data Source Name) فقط، وإلا قد يكون تطبيقك معرّضًا لثغرة ضعيفة. إنّ ضبط ترميز المحارف ليكون DSN غير متاح في الإصدارات PDO السابقة للإصدار 5.3.6 لذا فالخيار الوحيد هو ضبط سمة الاتصال PDO::ATTR_EMULATE_PREPARES لتكون false مباشرةً بعد إنشائه.

$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

ستؤدي هذه الشيفرة إلى استخدام PDO لتعليمات التحضير الأساسية الموجودة في DBMS (‏Database Management Systems) بدلًا من محاكاتها فقط، وانتبه إلى أنّ PDO ستتراجع ببطء لمحاكاة التعليمات التي لا تستطيع MySQL تحضيرها محليًّا، هذه العبارات موجودة في التوثيق.

اتصال PDO الأساسي والاسترجاع

بدءًا من الإصدار PHP 5.0 أصبحت PDO متاحة كطبقة وصول إلى قاعدة البيانات، وهي لا تعرف قاعدة البيانات، ستعمل الشيفرة في مثال الاتصال التالي لأي من قواعد البيانات المدعومة ببساطة عن طريق تغيير الترميز DSN.

// (1)
$dsn = "mysql:host=localhost;dbname=testdb;charset=utf8";

// (2)
$username = "user";
$password = "pass";
$db = new PDO($dsn, $username, $password);

// ‫ضبط PDO لرمي استثناء إذا أُدخِل استعلام غير صحيح
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// تحضير تعليمة التنفيذ مع عنصر بديل مفرد
$query = "SELECT * FROM users WHERE class = ?";
$statement = $db->prepare($query);

// إنشاء بعض المعاملات لملء العناصر البديلة وتنفيذ التعليمة
$parameters = [ "221B" ];
$statement->execute($parameters);

// التكرار على كل سجل كمصفوفة ترابطية
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
    do_stuff($row);
}

ننشئ في الموضع (1) مقبضًا لقاعدة البيانات ونستخدم MySQL (اتصال باستخدام مقبس محلي) يوضّح الموضع (2) أنّه يمكنك استخدام MySQL (اتصال عبر الشبكة، يمكنك تحديد المنفذ أيضًا إذا أردت) باستخدام الشيفرة:

$dsn = "mysql:host=127.0.0.1;port=3306;dbname=testdb;charset=utf8";

أو استخدام Postgres:

$dsn = "pgsql:host=localhost;port=5432;dbname=testdb;";

أو حتى SQLite:

$dsn = "sqlite:/path/to/database"

تنشئ الدالة prepare كائنًا من الصنف PDOStatement من سلسلة الاستعلام، تجري عمليات تنفيذ الاستعلام واسترجاع النتائج على هذا الكائن المُرجَع. في حال الفشل إما تعيد الدالة false أو ترمي استثناءً (بالاعتماد على كيفية ضبط إعدادات اتصال PDO).

عمليات قاعدة البيانات مع PDO

تضمن عمليات (transactions) قاعدة البيانات أنَّ مجموعة التغييرات على قاعدة البيانات ستصبح دائمة فقط إذا نجحت كل التعليمات، يمكن التقاط أي فشل في الاستعلام أو الشيفرة خلال عملية ما ثم يكون لديك خيار التراجع (roll back) عن التغييرات التي حاولت القيام بها. توفر PDO توابع بسيطة لعمليات البدء وحفظ التغييرات (committing) والتراجع.

$pdo = new PDO(
    $dsn,
    $username,
    $password,
    array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);

try {
    $statement = $pdo->prepare("UPDATE user SET name = :name");
    $pdo->beginTransaction();
    $statement->execute(["name"=>'Bob']);
    $statement->execute(["name"=>'Joe']);
    $pdo->commit();
}

catch (\Exception $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollback();
        // إذا وصلنا إلى هنا فإنَّ التحديثين ليسا في قاعدة البيانات
    }
    throw $e;
}

تكون أي تغييرات تُجرى على البيانات خلال العملية مرئية للاتصال النشط، ستُرجع تعليمات SELECT التغييرات المُعدَّلة حتى لو لم تُحفظ في قاعدة البيانات بعد.

مثال عملي لاستخدام العمليات مع PDO

نعرض لك في الفقرة التالية مثال عملي من العالم الحقيقي حيث يضمن استخدام العمليات اتساق قاعدة البيانات.

تخيل بأنّك تبني عربة تسوق لموقع تجارة الكترونية وقررت أن تحتفظ بطلبات العملاء في جدولين من قاعدة البيانات، اسم الجدول الأول orders يعبر عن الطلبات وحقوله هي order_id، ‏name، ‏address، ‏telephone و‏created_at، واسم الجدول الثاني orders_products يحوي منتجات الطلبات وحقوله هي order_id،‏product_id وquantity، أي يحتوي الجدول الأول على بيانات وصفية للطلب والجدول الثاني على المنتجات الفعلية التي طلبها العملاء.

إضافة طلب جديد إلى قاعدة البيانات

تحتاج لإضافة طلب جديد إلى قاعدة البيانات إلى أمرين، الأول هو إنشاء سجل جديد (INSERT) في الجدول orders يحتوي على البيانات الوصفية للطلب (الاسم والعنوان وغير ذلك)، والثاني هو إنشاء سجل جديد في الجدول orders_products لكل منتج من المنتجات الموجودة في قائمة الطلب. يمكنك القيام بذلك باستخدام الشيفرة التالية:

// إضافة البيانات الوصفية للطلب في قاعدة البيانات
$preparedStatement = $db->prepare(
    'INSERT INTO `orders` (`name`, `address`, `telephone`, `created_at`)
    VALUES (:name, :address, :telephone, :created_at)'
);

$preparedStatement->execute([
    'name' => $name,
    'address' => $address,
    'telephone' => $telephone,
    'created_at' => time(),
]);

// ‫الحصول على `order_id` المولَّد
$orderId = $db->lastInsertId();

// بناء استعلام لإضافة منتجات الطلب
$insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`, `product_id`, `quantity`) VALUES';

$count = 0;
foreach ( $products as $productId => $quantity ) {
    $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ', :quantity' . $count . ')';
    $insertProductsParams['order_id' . $count] = $orderId;
    $insertProductsParams['product_id' . $count] = $productId;
    $insertProductsParams['quantity' . $count] = $quantity;

    ++$count;
}

// إضافة المنتجات المُضمَّنة في الطلب إلى قاعدة البيانات
$preparedStatement = $db->prepare($insertProductsQuery);
$preparedStatement->execute($insertProductsParams);

ستعمل هذه الشيفرة بشكلٍ رائع على إضافة طلب جديد إلى قاعدة البيانات حتى يحدث شيء ما غير متوقع ولسببٍ ما يفشل استعلام INSERT الثاني وعندها سيكون لديك طلب جديد في الجدول orders بدون وجود منتجات مرتبطة فيه من الجدول orders_products. لحسن الحظ يمكن إصلاح هذا ببساطة بجعل الاستعلامين في عملية قاعدة بيانات واحدة.

إضافة طلب جديد إلى قاعدة البيانات مع عملية

كل ماعليك فعله لبدء عملية باستخدام PDO هو استدعاء التابع beginTransaction قبل تنفيذ أي استعلامات على قاعدة البيانات، ثم تجري التغييرات التي تريدها بتنفيذ استعلامات INSERT و/أو UPDATE وفي النهاية تستدعي التابع commit للكائن لجعل التغييرات دائمة، قبل استدعاء التابع commit فإنّ كل التغييرات التي تجريها على بياناتك غير دائمة ويمكن التراجع عنها بسهولة باستدعاء التابع rollback للكائن PDO.

نوضح في المثال التالي استخدام العمليات لإضافة طلب جديد في قاعدة البيانات مع ضمان اتساق البيانات في نفس الوقت، ستُرجع جميع التغييرات إذا فشل أحد الاستعلامين، نستخدم في هذا المثال MySQL لكن يمكن تطبيقه على أي قاعدة بيانات تدعم العمليات.

$db = new PDO('mysql:host=' . $host . ';dbname=' . $dbname . ';charset=utf8', $username,$password);

// ‫تأكد من أنّ PDO سترمي استثناءً في حالة الخطأ لجعل عملية معالجة الخطأ أسهل
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    // بدءًا من هذه النقطة وحتى تُحفظ العملية يمكن التراجع عن كل تغيير في قاعدة البيانات
    $db->beginTransaction();

    // إضافة البيانات الوصفية للطلب في قاعدة البيانات
    $preparedStatement = $db->prepare(
        'INSERT INTO `orders` (`order_id`, `name`, `address`, `created_at`)
        VALUES (:name, :address, :telephone, :created_at)'
    );

    $preparedStatement->execute([
        'name' => $name,
        'address' => $address,
        'telephone' => $telephone,
        'created_at' => time(),
    ]);

    // ‫الحصول على `order_id` المولَّد
    $orderId = $db->lastInsertId();

    // بناء الاستعلام لإضافة منتجات الطلب
    $insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`,     `product_id`, `quantity`) VALUES';

    $count = 0;
    foreach ( $products as $productId => $quantity ) {
        $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ',     :quantity' . $count . ')';

        $insertProductsParams['order_id' . $count] = $orderId;
        $insertProductsParams['product_id' . $count] = $productId;
        $insertProductsParams['quantity' . $count] = $quantity;
        ++$count;
    }

    // إضافة منتجات الطلب إلى قاعدة البيانات
    $preparedStatement = $db->prepare($insertProductsQuery);
    $preparedStatement->execute($insertProductsParams);

    // جعل تغييرات قاعدة البيانات دائمة
    $db->commit();
}
catch ( PDOException $e ) {
    // فشل إضافة الطلب في قاعدة البيانات لذا نتراجع عن التغييرات
    $db->rollback();
    throw $e;
}

الاتصال بخادم MySQL/MariaDB باستخدام PDO

يوجد طريقتين للاتصال بخادم MySQL/MariaDB وفقًا للبنية التحتية.

الاتصال (TCP/IP) المعياري

$dsn = 'mysql:dbname=demo;host=server;port=3306;charset=utf8';
$connection = new \PDO($dsn, $username, $password);

// ‫رمي استثناءات عند حدوث خطأ SQL
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

// منع محاكاة تعليمات التحضير
$connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

يجب أن تعطّل المحاكاة بشكلٍ واضح لأنّ PDO صممت لتكون متكاملة مع إصدارات خادم MySQL الأقدم (التي لا تدعم تعليمات التحضير)، وإلا ستخسر فوائد منع الحقن (injection prevention) المضافة التي يوفرها عادةً استخدام تعليمات التحضير.

سلوك معالجة الخطأ الافتراضي هو حل وسط آخر، إذا لم يُعَدّ فلن تظهر PDO أي مؤشرات إلى أخطاء SQL، يُنصح بضبطه إلى الإعداد "exception mode" (نمط الاستثناء) لأنّه يمنحك وظائف إضافية عند كتابة تعابير ثبات مجردة (مثلًا عندما يكون لديك استثناء عند انتهاك القيد UNIQUE).

اتصال المقبس

$dsn = 'mysql:unix_socket=/tmp/mysql.sock;dbname=demo;charset=utf8';
$connection = new \PDO($dsn, $username, $password);

// ‫رمي استثناءات عند حدوث خطأ SQL
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

// منع محاكاة تعليمات التحضير
$connection->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);

في الأنظمة مثل unix إذا كان اسم المضيف 'localhost' يتم الاتصال بالخادم عبر مقبس المجال (domain socket).

الحصول على عدد الأسطر المتأثرة بالاستعلام باستخدام PDO

نبدأ بالمتغير ‎$db والذي هو نسخة من الصنف PDO. نريد غالبًا بعد تنفيذ الاستعلام أن نحدد عدد الأسطر المتأثرة بهذا الاستعلام عندها نستخدم التابع rowCount()‎ من الصنف PDOStatement.

$query = $db->query("DELETE FROM table WHERE name = 'John'"); $count = $query->rowCount(); echo "Deleted $count rows named John";

ملاحظة: يُستخدم هذا التابع لتحديد عدد الأسطر المتأثرة بتعليمات INSERT وDELETE وUPDATE فقط، بالرغم من أنّ هذا التابع قد يعمل بشكلٍ صحيح مع تعليمات SELECT أيضًا إلا أنّه غير متسق مع جميع قواعد البيانات.

PDO::lastInsertId()‎

يمكنك الحصول على قيمة المعرِّف (ID) المتزايدة تلقائيًا للسطر الذي أضفته إلى جدول قاعدة البيانات باستخدام التابع lastInsertId()‎.

// ‫فتح اتصال أساسي (MySQL)
$host = 'localhost';
$database = 'foo';
$user = 'root'
$password = '';
$dsn = "mysql:host=$host;dbname=$database;charset=utf8";
$pdo = new PDO($dsn, $user, $password);

// ‫إضافة سطر جديد في الجدول الافتراضي 'foo_user'
$query = "INSERT INTO foo_user(pseudo, email) VALUES ('anonymous', 'anonymous@example.com')";
$query_success = $pdo->query($query);

// استعادة المعرِّف للسطر الأخير المدخل
$id = $pdo->lastInsertId();
// القيمة المعادة هي عدد صحيح

لدينا الكلمة المفتاحية RETURNING في قواعد البيانات postgresql وoracle والتي تُرجع الأعمدة المحددة للأسطر المُدخلة/المعدّلة حاليًا. إليك مثال لإضافة سطر جديد واحد:

// ‫‫فتح اتصال أساسي (PGSQL)
$host = 'localhost';
$database = 'foo';
$user = 'root'
$password = '';
$dsn = "pgsql:host=$host;dbname=$database;charset=utf8";
$pdo = new PDO($dsn, $user, $password);

// ‫إضافة سطر جديد في الجدول الافتراضي 'foo_user'
$query = "INSERT INTO foo_user(pseudo, email) VALUES ('anonymous', 'anonymous@example.com') RETURNING id";
$statement = $pdo->query($query);

// استعادة المعرِّف للسطر الأخير المدخل
$id = $statement->fetchColumn();

استخدام الإضافة SQLSRV في PHP

استعادة رسائل الخطأ

من المهم جلب رسالة (أو رسائل) الخطأ المُرجعة من قِبل المُشغّل عند حدوث خطأ ما وذلك لمعرفة سبب المشكلة، الصيغة هي:

sqlsrv_errors([int $errorsOrWarnings]);

تُرجع هذه الشيفرة مصفوفة مع مفتاح ووصف.

المفتاح الوصف
SQLSTATE ‫ حالة خادم SQL/ مشغّل OBDC
الشيفرة SQL Server شيفرة خطأ
الرسالة وصف الخطأ

من الشائع استخدام الدالة السابقة كالتالي:

$brokenQuery = "SELECT BadColumnName FROM Table_1";
$stmt = sqlsrv_query($conn, $brokenQuery);

if ($stmt === false) {
    if (($errors = sqlsrv_errors()) != null) {
        foreach ($errors as $error) {
            echo "SQLSTATE: ".$error['SQLSTATE']."<br />";
            echo "code: ".$error['code']."<br />";
            echo "message: ".$error['message']."<br />";
        }
    }
}

جلب نتائج الاستعلام

يوجد 3 طرق أساسية لجلب النتائج من استعلام:

  • sqlsrv_fetch_array()‎

تعيد هذه الدالة السطر التالي كمصفوفة.

$stmt = sqlsrv_query($conn, $query);

while($row = sqlsrv_fetch_array($stmt)) {
    echo $row[0];
    $var = $row["name"];
    //...
}

لهذه الدالة معامل ثاني اختياري لجلب أنواع مختلفة من المصفوفات، يمكن أن يكون SQLSRV_FETCH_ASSOC أو SQLSRV_FETCH_NUMERIC أو SQLSRV_FETCH_BOTH (القيمة الافتراضية) ويعيد مصفوفة ترابطية، عددية، أو مصفوفة ترابطية وعددية على الترتيب.

  • sqlsrv_fetch_object()‎

تعيد هذه الدالة السطر التالي ككائن.

$stmt = sqlsrv_query($conn, $query);
while($obj = sqlsrv_fetch_object($stmt)) {
    // أسماء خاصيات الكائن هي أسماء حقول الاستعلام
    echo $obj->field;
    //...
}
  • sqlsrv_fetch()‎

تجعل هذه الدالة السطر التالي متاحًا للقراءة.

$stmt = sqlsrv_query($conn, $query);
while(sqlsrv_fetch($stmt) === true) {
    // الحصول على الحقل الأول
    $foo = sqlsrv_get_field($stmt, 0); 
}

إنشاء اتصال

// ‫اسم الخادم/النسخة، متضمنًا رقم منفذ اختياري (الافتراضي 1433)
$dbServer = "localhost,1234";

// اسم قاعدة البيانات
$dbName = "db001";

// اسم المستخدم
$dbUser = "user";

// كلمة مرور قاعدة البيانات لهذا المستخدم
$dbPassword = "password";

$connectionInfo = array(
    "Database" => $dbName,
    "UID" => $dbUser,
    "PWD" => $dbPassword
);
$conn = sqlsrv_connect($dbServer, $connectionInfo);

للإضافة SQLSRV أيضًا مشغّل PDO. للاتصال باستخدام PDO:

$conn = new PDO("sqlsrv:Server=localhost,1234;Database=db001", $dbUser, $dbPassword);

كتابة استعلام بسيط

// إنشاء اتصال
$conn = sqlsrv_connect($dbServer, $connectionInfo);

$query = "SELECT * FROM [table]";
$stmt = sqlsrv_query($conn, $query);

ملاحظة: نستخدم الأقواس المربعة [] للهرب من الكلمة المحجوزة ‏table، وتعمل نفس عمل علامة الاقتباس المائلة ` في MySQL.

استدعاء إجراء مخزن (Stored Procedure)

لاستدعاء إجراء مخزن على الخادم:

// المعاملات '?' تتضمن معاملات خرج
$query = "{call [dbo].[myStoredProcedure](?,?,?)}";

$params = array(
    array($name, SQLSRV_PARAM_IN),
    array($age, SQLSRV_PARAM_IN),
    // ‏يجب أن يكون المتغير ‎$count معرّف مسبقًا
    array($count, SQLSRV_PARAM_OUT, SQLSRV_PHPTYPE_INT)
);

$result = sqlsrv_query($conn, $query, $params);

كتابة استعلام ذو معاملات

$conn = sqlsrv_connect($dbServer, $connectionInfo);

$query = "SELECT * FROM [users] WHERE [name] = ? AND [password] = ?";
$params = array("joebloggs", "pa55w0rd");

$stmt = sqlsrv_query($conn, $query, $params);

إذا كنت تخطط لاستخدام نفس تعليمة الاستعلام أكثر من مرة مع معاملات مختلفة فيمكنك تحقيق ذلك باستخدام الدوال sqlsrv_prepare()‎ وsqlsrv_execute()‎ كما في الشيفرة التالية:

$cart = array(
    "apple" => 3,
    "banana" => 1,
    "chocolate" => 2
);

$query = "INSERT INTO [order_items]([item], [quantity]) VALUES(?,?)";
// المتغيرات هي معاملات يجب تمريرها بالمرجع
$params = array(&$item, &$qty);

$stmt = sqlsrv_prepare($conn, $query, $params);
foreach($cart as $item => $qty){
    if(sqlsrv_execute($stmt) === FALSE) {
        die(print_r(sqlsrv_errors(), true));
    }
}

ترجمة -وبتصرف- للفصول [PDO - Using MongoDB - mongo-php - Using Redis with PHP - Using SQLSRV] من كتاب PHP Notes for Professionals book

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...