دمج Magento مع Cloud Spanner

دمج Magento مع Cloud Spanner

لمحة عن هذا الدرس التطبيقي حول الترميز

subjectتاريخ التعديل الأخير: يناير 5, 2022
account_circleتأليف: Derek Downey

1. مقدمة

424db48d9db91638.png

دمج Magento مع خلفية Cloud Spanner

Magento هو نظام أساسي للتجارة الإلكترونية مفتوحة المصدر يستنِد إلى لغة PHP ويخزّن البيانات في MySQL.

يُعد هذا الدرس التطبيقي حول الترميز بمثابة إثبات مفهوم للاستفادة من Cloud Spanner بدلاً من MySQL في وحدة Catalog. ويُعد هذا مفيدًا لأي شخص مهتم بدمج تطبيقات Magento أو تطبيقات PHP الأخرى واختبارها ونشرها باستخدام Spanner.

Spanner هي قاعدة بيانات Google Cloud مُدارة بالكامل على مستوى المؤسسة وموزَّعة ومتسقة تجمع بين مزايا نموذج قاعدة البيانات العلائقية وقابلية التوسّع الأفقي غير العلائقية. صُممت هذه الواجهة لدعم عمليات النشر العالمية لمعالجة المعاملات عبر الإنترنت، ودلالات SQL، والتوسيع الأفقي المتاح إلى حد كبير، واتساق المعاملات. بإمكان Spanner معالجة كميات كبيرة من البيانات. ولا يقتصر استخدامه على التطبيقات ذات الحجم الكبير ولكنه يتيح توحيد محرك قاعدة بيانات واحد لجميع أحمال العمل التي تتطلب نظام RDBMS. يوفر Spanner عدم توقف عن العمل للصيانة المخطَّط لها أو أعطال المنطقة، مع اتفاقية مستوى الخدمة لتوفُّر الخدمة بنسبة%99.999. ويدعم التطبيقات الحديثة من خلال توفير مستوى عالٍ من التوافر وقابلية التوسع.

ما ستتعرَّف عليه

  • كيفية تثبيت Magento على GCE
  • كيفية إعداد "محاكي Spanner"
  • كيفية نقل مخطط MySQL حالي إلى Spanner باستخدام HarbourBridge
  • ما يجب تغييره لدمج تطبيقات PHP مثل Magento التي تستخدم MySQL لخلفية قاعدة البيانات للعمل مع Spanner

ما الذي ستقوم ببنائه

يركّز هذا الدرس التطبيقي حول الترميز على دمج Magento مع Spanner. يتم توفير مجموعات الرموز وتعليمات الإعداد لنسخها ولصقها، ولكن لا نناقشها بالتفصيل.

في هذا الدرس التطبيقي، ستبدأ في دمج Magento مع Spanner. وسوف تقوم بما يلي:

  • إعداد مثيل GCE مع تثبيت Magento
  • تثبيت محاكي Sppanner
  • تثبيت HarbourBridge Tool لنقل البيانات من MySQL إلى Spanner
  • يُرجى تعديل مجموعات Magento لتحميل كتالوج المنتجات من Spanner.

المتطلبات

  • مشروع على Google Cloud مرتبط بحساب فوترة
  • تمثل معرفة إعدادات PHP وLinux وApache ميزة إضافية.
  • ستكون تجربة Magento مفيدة، ولكنها غير مطلوبة.

2. جارٍ تحضير مثيل GCE

إنشاء مثيل GCE

أنشئ مثيل Compute Engine في Google Cloud Platform باتّباع الخطوات المذكورة هنا.

عند إنشاء مثيل GCE، غيِّر نوع المثيل إلى e2-standard-2 وحجم قرص التشغيل إلى 20 غيغابايت. يمكنك ترك كل شيء كإعداد تلقائي، ولكن يجب التأكّد من اختيار "السماح بالزيارات من HTTP". و"السماح بحركة مرور HTTPs"، لأننا نستفيد من واجهة الويب في Magento.

ينتج عن ذلك نوع الجهاز e2-standard-2، وهو ليس مثيلاً أساسيًا مشتركًا ويضم 2 وحدة معالجة مركزية افتراضية وذاكرة وصول عشوائي (RAM) بسعة 8 غيغابايت و20 غيغابايت.

نظام التشغيل هو Debian 10. قد يستغرق إنشاء المثيل دقيقة أو دقيقتين.

بعد إنشائه، يمكنك المتابعة وتسجيل الدخول عن طريق النقر على "بروتوكول النقل الآمن" (SSH) في Cloud Console:

4bf915ef8d37c942.png

سيؤدي ذلك إلى فتح نافذة متصفح جديدة ووضعك في وحدة طرفية.

تثبيت البرنامج المطلوب

سيحتاج Magento إلى تثبيت بعض البرامج الأساسية قبل أن نتمكن من تشغيل Magento. وعلى وجه التحديد، ستقوم بتثبيت PHP وElastic وMySQL وApache كما هو موضح أدناه.

  1. تثبيت بعض الحِزم المطلوبة.
sudo apt update

sudo apt -y install lsb-release apt-transport-https ca-certificates wget git screen composer google-cloud-sdk-spanner-emulator gcc
  1. تثبيت وحدات PHP المطلوبة لبرنامج Magento.
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg

echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list

sudo apt update

sudo apt -y install php7.4-fpm php7.4-common php7.4-mysql php7.4-gmp php7.4-curl php7.4-intl php7.4-mbstring php7.4-xmlrpc php7.4-gd php7.4-xml php7.4-cli php7.4-zip php7.4-bcmath php7.4-soap php7.4-grpc
  1. تثبيت Elasticsearch وبدء الخدمة
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list

sudo apt update && sudo apt -y install elasticsearch

echo "-Xms1g
-Xmx1g" | sudo tee /etc/elasticsearch/jvm.options.d/jvm.options

sudo systemctl start elasticsearch.service
  1. تثبيت MySQL

أنت تقوم بتثبيت MySQL للحصول على مخطط Magento الافتراضي مثبتًا. ويمكنك لاحقًا نقل المخطط إلى Spanner باستخدام HarbourBridge.

wget https://dev.mysql.com/get/mysql-apt-config_0.8.13-1_all.deb

sudo dpkg -i mysql-apt-config*

سيعرِض الأمر dpkg أعلاه طلبًا تفاعليًا لتثبيت خادم MySQL 5.7. حدِّد الخيارات التالية:

  • MySQL Server المجموعة
  • mysql-5.7
  • حسنًا

a018bfc2ee00bdf5.png 1a126e452ca7312e.png ae39c6f4bbe3be74.png

sudo apt update && sudo apt -y install mysql-server
# You will be prompted to enter a root password
  1. تثبيت Apache2
sudo apt -y install apache2

sudo a2enmod proxy_fcgi rewrite

تثبيت وتهيئة Magento2

يتضمن مشروع Magento Commerce Cloud مخطط قاعدة بيانات وخدمات للوصول بشكل كامل إلى موقع Magento ومتجره.

أسهل طريقة لتثبيت هذا وتشغيله هي اتباع تعليمات Magento للتثبيت باستخدام composer:

  1. تثبيت الإصدار 2.4.2 من Magento باستخدام المؤلف. يتطلب Magento 2 إصدار المؤلف 1.x. قد تظهر لك بعض التحذيرات بشأن إيقاف دعم هذا الإصدار.
composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition=2.4.2 magento2
  1. ضبط أذونات المجلدات
cd magento2

find var generated vendor pub/static pub/media app/etc -type f -exec chmod g+w {} +

find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} +
  1. اضبط مضيف Magento الافتراضي من خلال إنشاء /etc/apache2/sites-available/magento.conf باستخدام المحتوى أدناه.
sudo nano /etc/apache2/sites-available/magento.conf

<VirtualHost *:80>
        ServerAdmin admin@local-magento.com
        DocumentRoot /var/www/html/magento/

        <Directory /var/www/html/magento/>
                Options Indexes FollowSymlinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

        <FilesMatch \.php$>
               SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
        </FilesMatch>

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
  1. أنشِئ الروابط الرمزية وأعِد تشغيل Apache2.
cd ~/magento2
sudo ln -s $(pwd) /var/www/html/magento 
sudo ln -s /etc/apache2/sites-available/magento.conf  /etc/apache2/sites-enabled/magento.conf
sudo rm /etc/apache2/sites-enabled/000-default.conf

sudo systemctl restart apache2
  1. إنشاء قاعدة البيانات والمستخدم لـ Magento في MySQL
export ROOT_PASSWORD="<root password from installation>"
export GCE_INSTANCE_IP="<GCE instance IP>"
mysql -uroot -p$ROOT_PASSWORD -e "create database magento"

bin/magento sampledata:deploy

bin/magento setup:install --base-url=http://$GCE_INSTANCE_IP/ --db-host=localhost \
--db-name=magento --db-user=root --db-password=$ROOT_PASSWORD --admin-firstname=admin \
--admin-lastname=demo --admin-email=good@example.com --admin-user=admin \
--admin-password=magento123 --language=en_US --currency=USD --timezone=America/Chicago \
--use-rewrites=1

sudo chown -R :www-data ~/magento2/. 
  1. تحقَّق من مساحة العمل المحلية للتأكّد من أنّ البيئة المحلية تستضيف الخادم، يمكنك الدخول إلى المتجر باستخدام عنوان URL الأساسي الذي أدخلته في الأمر install. في هذا المثال، يمكنك الوصول إلى متجر Magento المحلي باستخدام تنسيقات عناوين URL التالية:
  • http://&lt;GCEexternalIP&gt;/
  • http://&lt;GCEexternalIP&gt;/&lt;adminuri&gt;

يمكن العثور على GCEexternalIP في Cloud Console:

3947f1164e1d5409.png

لتغيير معرِّف الموارد المنتظم (URI) للوحة المشرف، استخدِم الأمر التالي لتحديد موقعه:

php bin/magento info:adminuri
  1. إيقاف ذاكرة التخزين المؤقت للصفحة بالكامل لأغراض التطوير، يمكنك إيقاف ذاكرة التخزين المؤقت للصفحة الكاملة في Magento2. سيتيح لك ذلك تعديل البيانات في Spanner وإظهارها على الموقع الإلكتروني بدون التأثير على القيم المخزّنة مؤقتًا.
php bin/magento cache:disable full_page

إعداد Spanner

تثبيت محاكي Spanner

توفّر حزمة تطوير البرامج (SDK) للسحابة الإلكترونية محاكيًا محليًا في الذاكرة يمكنك استخدامه لتطوير تطبيقاتك واختبارها مجانًا بدون إنشاء مشروع Google Cloud Platform أو حساب فوترة. ونظرًا لأن المحاكي يخزِّن البيانات في الذاكرة فقط، تُفقد جميع الحالات عند إعادة التشغيل، بما في ذلك البيانات والمخطط والتكوينات. يوفّر المحاكي واجهات برمجة التطبيقات نفسها المتوفرة في خدمة الإنتاج على Spanner، وهو مخصّص للتطوير والاختبار على الجهاز، وليس لعمليات نشر الإنتاج.

يُرجى استخدام الرابط أدناه لمزيد من الإرشادات لتثبيت المحاكي واستخدامه ونشره:

استخدام "محاكي Spanner"

# Set up a new configuration to use the emulator
gcloud config configurations create emulator
gcloud config set auth/disable_credentials true
gcloud config set project magento
gcloud config set api_endpoint_overrides/spanner http://localhost:9020/

# Start emulator in a screen session
screen -S magento
gcloud emulators spanner start &
gcloud spanner instances create magento-instance --config=emulator-config --description='Magento Instance' --nodes=1

# Detach from screen 
ctrl+a+d

export SPANNER_EMULATOR_HOST=localhost:9010

نقل بيانات Magento MySQL إلى Spanner

قبل التعمّق في دمج Spanner، سنستخدم أداة تُسمى HarbourBridge لتحويل قاعدة بيانات MySQL التي تم إنشاؤها كجزء من تثبيت Magento أعلاه إلى Spanner.

يوفّر HarbourBridge في جوهره سير عمل آليًّا لتحميل محتوى قاعدة بيانات MySQL أو PostgreSQL الحالية إلى Spanner. ولا يتطلب ذلك أي إعداد، ولا تتوفّر أي بيانات أو خرائط بيانات للكتابة. وبدلاً من ذلك، تستورد هذه الخدمة قاعدة بيانات المصدر وتنشئ مخطط Spanner، وتنشئ قاعدة بيانات Spanner جديدة مليئة ببيانات من قاعدة بيانات المصدر، وتنشئ تقريرًا تفصيليًا للتقييم. تم تصميم HarbourBridge لتحميل قواعد البيانات التي يصل حجمها إلى بضع عشرات غيغابايت لأغراض التقييم، وليس لعمليات الترحيل على النطاق الكامل.

يعمل HarbourBridge على بدء عملية النقل في مرحلة مبكرة إلى Spanner من خلال استخدام قاعدة بيانات مصدر MySQL أو PostgreSQL الحالية لمساعدتك على بدء استخدام Spanner بسرعة. تنشئ هذه الأداة تقرير تقييم يتضمن نتيجة إجمالية لملاءمة نقل البيانات لـ Spanner، وتحليل جدول بجدول لعمليات ربط الأنواع، وقائمة بالميزات المستخدمة في قاعدة بيانات المصدر التي لا تتوافق مع Spanner.

يمكن استخدام HarbourBridge مع Spanner Emulator، أو مباشرةً مع مثيل Spanner.

يحتوي HarbourBridge README على دليل البدء السريع مفصّل لاستخدام الأداة مع مثيل Spanner.

تثبيت HarbourBridge

نزِّل الأداة على جهازك وثبِّتها. يجب تثبيت golang لكي تعمل هذه الميزة. قد يستغرق تثبيت جميع الوحدات المطلوبة على مثيل جديد بعض الوقت بدون أن يتم إعداده مسبقًا.

# Install golang
cd ~
wget https://golang.org/dl/go1.17.2.linux-amd64.tar.gz
sudo tar -zxvf go1.17.2.linux-amd64.tar.gz -C /usr/local
rm go1.17.2.linux-amd64.tar.gz

echo 'export GOROOT=/usr/local/go' | sudo tee -a /etc/profile
echo 'export PATH=/usr/local/go/bin:$HOME/go/bin:$PATH' | sudo tee -a /etc/profile
source /etc/profile

# Install harbourbridge
git clone https://github.com/cloudspannerecosystem/harbourbridge
cd harbourbridge
go run github.com/cloudspannerecosystem/harbourbridge help

نقل البيانات

استخدم الأمر التالي لنقل قاعدة بيانات Magento إلى Spanner:

mysqldump --user='root' --password=$ROOT_PASSWORD magento | go run github.com/cloudspannerecosystem/harbourbridge -driver=mysqldump -dbname=magento

إعداد أداة مفتاح الربط

go install github.com/cloudspannerecosystem/spanner-cli@latest

3. تحويل Magento للعمل مع Spanner

الآن وبعد تشغيل Magento، وتم نقل مثيل Spanner الذي تم إنشاؤه مع قاعدة بيانات Magento، سنعمل على تعديل Magento للعمل مع البيانات المخزنة في Spanner.

سيتم تنفيذ الخطوات التالية لتحويل تثبيت Magento:

  • استنسِخ مشروع منفذ Magento-spanner-port
  • تغيير الاتصال بـ Spanner
  • التحقّق من تعبئة تفاصيل الكتالوج من Spanner

استنساخ شوكة مشروع Magento

استنسِخ رمز تطبيق PHP لتطبيق Magento الذي يحتوي على التعديلات لوحدات الكتالوج وقائمة الأمنيات وعربة التسوق من عنوان URL Git المذكور أدناه.

cd ~
git clone https://github.com/searceinc/magento-spanner-port

يجب أن يبدو الدليل الرئيسي على النحو التالي:

$ ls
go  harbourbridge  magento-spanner-port  magento2

حيث سيكون magento2 هو قاعدة الرموز التي سنعدّلها، باستخدام الرمز من magento-spanner-port.

تغيير الاتصال بـ Spanner

للتحقق مما إذا كانت تعديلات الرموز تنعكس في واجهة المستخدم، يمكننا اتّباع الخطوات التالية -

يُرجى الرجوع إلى رابط GitHub https://github.com/searceinc/magento-spanner-port للاطّلاع على نموذج التنفيذ.

  • اشتراط مكتبة برامج google/cloud-spanner PHP
  • عليك إضافة محوِّل Spanner لإنشاء اتصال بأداة Spanner.
  • اضبِط معلومات مثيل Spanner والخادم.
  • يمكنك إضافة SpannerInterface وSpanner في المحوّل لتنفيذ الاتصال بـ Spanner.

أولاً، نحتاج إلى تثبيت مكتبة PHP لأداة ربط السحابة الإلكترونية باستخدام واجهة برمجة التطبيقات composer. في دليل magento2، شغِّل الأمر التالي:

cd ~/magento2
composer require google/cloud-spanner

بعد ذلك، نضيف ملفات Spanner Adapter من منفذ Magento-spanner إلى قاعدة الرموز magento2:

~/magento2$ cp -r ../magento-spanner-port/lib/internal/Magento/Framework/DB/Adapter/Spanner vendor/magento/framework/DB/Adapter/.
~/magento2$ ls -l vendor/magento/framework/DB/Adapter/Spanner
total 16
-rw-r--r-- 1 derekdowney derekdowney 10378 Nov  9 21:03 Spanner.php
-rw-r--r-- 1 derekdowney derekdowney  2948 Nov  9 21:03 SpannerInterface.php

والآن، عدِّل الملف DB/Adapter/Spanner/Spanner.php لإدخال معلومات اتصال Spanner لـ $project_id،$instance، و$database:

$ nano vendor/magento/framework/DB/Adapter/Spanner/Spanner.php

class Spanner implements SpannerInterface
{
    /**
     * Google cloud project id
     * @var string
     */
    private $project_id = 'magento';

    /**
     * Google cloud instance name
     * @var string
     */
    private $instance  = 'magento-instance';

    /**
     * Cloud Spanner database name
     * @var string
     */
    private $database  = 'magento';

    /**
     * Is Cloud Spanner emulator
     * @var bool
     */
    private $is_emulator = true;
...
   /**
    * Set database connection adapter
    *
    * @param \Magento\Framework\DB\Adapter\AdapterInterface $conn
    * @return $this
    * @throws \Magento\Framework\Exception\LocalizedException
    */
   public function setConnection(\Magento\Framework\DB\Adapter\AdapterInterface $conn)
   {
       $this->_conn = $conn;
       $this->_select = $this->_conn->select();
       $this->_isOrdersRendered = false;
       return $this;
   }


   /**
     * Set Cloud Spanner database connection adapter
     *
     * @return void
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function setSpannerConnection()
    {
        $this->_spanner_conn = new Spanner();
    }

عدِّل فئة AbstractDB في Magento للاتصال الآن بـ Spanner باستخدام وظيفة الاتصال التي تم إنشاؤها حديثًا ضمن محوِّل Spanner. أضف الخطوط الخضراء بعد الأسطر البيضاء في الملف. يُرجى الرجوع إلى vendor/magento/framework/Data/Collection/AbstractDb.php.

$ nano vendor/magento/framework/Data/Collection/AbstractDb.php
...
use Psr\Log\LoggerInterface as Logger;
use Magento\Framework\DB\Adapter\Spanner\Spanner;
...
    protected $_conn;

    /**
     * Cloud Spanner connection
     *
     * @var \Magento\Framework\DB\Adapter\Spanner\SpannerAdapterInterface
     */
    protected $_spanner_conn;
...
       if ($connection !== null) {
            $this->setConnection($connection);
        }
        $this->setSpannerConnection();
        $this->_logger = $logger;
...
   /**
     * Retrieve connection object
     *
     * @return AdapterInterface
     */
    public function getConnection()
    {
        return $this->_conn;
    }

   /**
     * Retrieve connection object
     *
     * @return SpannerAdapterInterface
     */
    public function getSpannerConnection()
    {
        return $this->_spanner_conn;
    }
...

بعد اكتمال الاتصال، نحتاج إلى تعديل طريقة استرجاع البيانات من محوّل MySQL إلى محوّل Spanner . عدِّل طريقة _loadAttributes في AbstractCollection للاتصال بـ Spanner وجلب البيانات من Spanner. استبدل الخط الأحمر بالخطوط الخضراء.

يُرجى الرجوع إلى /app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php.

$ nano ./vendor/magento/module-eav/Model/Entity/Collection/AbstractCollection.php

use Magento\Framework\Exception\LocalizedException;
use Google\Cloud\Spanner\SpannerClient;

...
               try {
                    if (is_array($selects)) {
                        $select = implode(' UNION ALL ', $selects);
                    } else {
                        $select = $selects;
                    }
                   $values = $this->getConnection()->fetchAll($select);
                   $con = $this->getSpannerConnection();

                    /**
                     * Cloud Spanner follows strict type so cast the columns in common type
                     */
                    $select = $con->addCast($select, "`t_d`.`value`", 'string');
                    $select = $con->addCast($select, "`t_s`.`value`", 'string');
                    $select = $con->addCast($select, "IF(t_s.value_id IS NULL, t_d.value, t_s.value)", 'string');
                    
                    $values = $con->fetchAll($select);

...

التحقّق من تعبئة تفاصيل الكتالوج من Spanner

هذا كل شيء! الآن، يمكنك الانتقال إلى تثبيت Magento في المتصفح والتحقق من تحميل البيانات.

على سبيل المثال، في ما يلي إدخالات الكتالوج للساعات:

13b54ba4482408fc.png

عليك تعديل بيانات Spanner من خلال الطرف الطرفية لأحد المنتجات، وطلب البيانات عبر الوحدة الطرفية لتأكيد التعديل في Spanner.

$ spanner-cli -pmagento -i magento-instance -d magento
spanner> SELECT * FROM catalog_product_entity_varchar WHERE value LIKE "Aim Analog%";
+----------+--------------+----------+-----------+--------------------+
| value_id | attribute_id | store_id | entity_id | value              |
+----------+--------------+----------+-----------+--------------------+
| 390      | 73           | 0        | 36        | Aim Analog Watch |
+----------+--------------+----------+-----------+--------------------+
1 rows in set (80.711542ms)

spanner> UPDATE catalog_product_entity_varchar SET value = "Aim Analog Spanner" WHERE value_id=390;
Query OK, 1 rows affected (0.19 sec)

spanner> SELECT * FROM catalog_product_entity_varchar WHERE value_id=390;
+----------+--------------+----------+-----------+--------------------+
| value_id | attribute_id | store_id | entity_id | value              |
+----------+--------------+----------+-----------+--------------------+
| 390      | 73           | 0        | 36        | Aim Analog Spanner |
+----------+--------------+----------+-----------+--------------------+
1 rows in set (80.711542ms)

يُرجى إعادة تحميل الشاشة للتأكّد من أنّه تم تغيير اسم الساعة إلى "Aim Analog Spanner". كما هو الحال عبر الوحدة الطرفية Spanner.

63a9c7b065c7051f.png

4. تهانينا

تهانينا، لقد نجحت في ربط وحدة الكتالوج من Magento للعمل مع Spanner. إنه ليس تكاملاً كاملاً، لكنك تعرف الآن العناصر اللازمة للحصول على تطبيق PHP مثل Magento متصل بمثيل Spanner.

التنظيف

عند اكتمال عملية إعداد جهة التواصل والتحقق من صحتها، قد تحتاج إلى حذف موارد Google Cloud Platform التي تم إنشاؤها أثناء العملية. ويشمل ذلك جهاز Compute Engine الافتراضي، بالإضافة إلى مثيل Cloud Spanner إذا قررت استخدام أحد هذه الأجهزة بدلاً من المحاكي.

ما هي الخطوات التالية؟

هذا مجرد نموذج أولي لجهة اتصال Spanner.

إذا أردت معرفة المزيد حول استخدام Spanner والتقنيات التي استفدنا منها في هذا الدرس التطبيقي حول الترميز، إليك بعض المراجع الإضافية: