Phalcon Query Language (PHQL)

Phalcon Query Language, PhalconQL atau hanya PHQL adalah bahasa tingkat tinggi, object-oriented SQL dialek yang memungkinkan untuk menulis query menggunakan standar SQL-seperti bahasa. PHQL diimplementasikan sebagai parser (ditulis dalam C) yang menerjemahkan sintaks dalam target RDBMS yg digunakan.

Untuk mencapai kinerja tertinggi, Phalcon menyediakan parser yang menggunakan teknologi yang sama dengan SQLite . Teknologi ini menyediakan parser kecil di memori dengan jejak memori yang sangat rendah yang juga string-safe.

Parser pertama memeriksa sintaks statemen PHQL yg diberikan, kemudian membangun sebuah representasi intermediate dr statement tsb dan akhirnya itu mengkonversi ke dialek SQL masing-masing target RDBMS.

Di PHQL, kami telah menerapkan serangkaian fitur untuk membuat akses Anda ke database lebih aman:

  • Bound Parameter merupakan bagian dari bahasa PHQL membantu Anda untuk mengamankan kode Anda
  • PHQL hanya memperbolehkan satu pernyataan SQL yang akan dieksekusi tiap kali dipanggil mencegah injeksi SQL
  • PHQL mengabaikan semua komentar SQL yang sering digunakan dalam injeksi SQL
  • PHQL hanya memungkinkan statemen manipulasi data (DML), menghindari alter atau drop tabel / database karna kesalahan atau eksternal tanpa otorisasi
  • PHQL mengimplementasikan abstraksi tingkat tinggi yang memungkinkan Anda untuk menangani tabel sebagai model dan field sebagai atribut class

Contoh Penggunaan  

Untuk menjelaskan lebih baik cara kerja PHQL perhatikanlah contoh berikut. Kami memiliki dua model “Cars” dan “Brands”:

<?php

class Cars extends Phalcon\Mvc\Model
{
    public $id;

    public $name;

    public $brand_id;

    public $price;

    public $year;

    public $style;

    /**
     * This model is mapped to the table sample_cars
     */
    public function getSource()
    {
        return 'sample_cars';
    }

    /**
     * A car only has a Brand, but a Brand have many Cars
     */
    public function initialize()
    {
        $this->belongsTo('brand_id', 'Brands', 'id');
    }
}

Dan setiap mobil memiliki Merek, sedangkan setiap Merek memiliki banyak Mobil:

<?php

class Brands extends Phalcon\Mvc\Model
{

    public $id;

    public $name;

    /**
     * The model Brands is mapped to the "sample_brands" table
     */
    public function getSource()
    {
        return 'sample_brands';
    }

    /**
     * A Brand can have many Cars
     */
    public function initialize()
    {
        $this->hasMany('id', 'Cars', 'brand_id');
    }
}

Membuat PHQL Query 

Query PHQL dapat dibuat hanya dengan mmebuat instance class Phalcon \ MVC \ Model \ Query :

<?php

// Instantiate the Query
$query = new Phalcon\Mvc\Model\Query("SELECT * FROM Cars", $this->getDI());

// Execute the query returning a result if any
$cars = $query->execute();

Dari controller ataupun view, sangat mudah untuk membuat / mengeksekusinya menggunakan sebuah injeksi manager model :

<?php

//Executing a simple query
$query = $this->modelsManager->createQuery("SELECT * FROM Cars");
$cars = $query->execute();

//With bound parameters
$query = $this->modelsManager->createQuery("SELECT * FROM Cars WHERE name = :name:");
$cars = $query->execute(array(
    'name' => 'Audi'
));

Atau hanya jalankan:

<?php

//Executing a simple query
$cars = $this->modelsManager->executeQuery("SELECT * FROM Cars");

//Executing with bound parameters
$cars = $this->modelsManager->executeQuery("SELECT * FROM Cars WHERE name = :name:", array(
    'name' => 'Audi'
));

Select Record 

Sebagaimana SQL umum digunakan, PHQL memungkinkan query record menggunakan pernyataan SELECT, kecuali bahwa alih-alih dgn menyebutkan tabelnya, kita menggunakan nama class modelnya:

<?php

$query = $manager->createQuery("SELECT * FROM Cars ORDER BY Cars.name");
$query = $manager->createQuery("SELECT Cars.name FROM Cars ORDER BY Cars.name");

Class di namespace juga diperbolehkan:

<?php

$phql = "SELECT * FROM Formula\Cars ORDER BY Formula\Cars.name";
$query = $manager->createQuery($phql);

$phql = "SELECT Formula\Cars.name FROM Formula\Cars ORDER BY Formula\Cars.name";
$query = $manager->createQuery($phql);

$phql = "SELECT c.name FROM Formula\Cars c ORDER BY c.name";
$query = $manager->createQuery($phql);

Sebagian besar bahasa standar SQL didukung oleh PHQL, bahkan arahan yang tidak standar semisal LIMIT:

<?php

$phql   = "SELECT c.name FROM Cars AS c "
   . "WHERE c.brand_id = 21 ORDER BY c.name LIMIT 100";
$query = $manager->createQuery($phql);

Tipe Result 

Tergantung pada jenis kolom kita query, jenis hasil akan bervariasi. Jika Anda mengambil satu objek secara keseluruhan, maka objek kembali adalah Phalcon \ MVC \ Model \ ResultSet \ Simple . Semacam ini resultset adalah satu set objek model yang lengkap:

<?php

$phql = "SELECT c.* FROM Cars AS c ORDER BY c.name";
$cars = $manager->executeQuery($phql);
foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

Hal ini persis sama dengan:

<?php

$cars = Cars::find(array("order" => "name"));
foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

/////////

Objek yg telah lengkap dapat dimodifikasi dan disimpan lagi dalam database karena objek tsb mewakili record lengkap dari tabel yang terkait. Ada jenis query lain yang tidak mengembalikan objek lengkap, misalnya:

<?php

$phql = "SELECT c.id, c.name FROM Cars AS c ORDER BY c.name";
$cars = $manager->executeQuery($phql);
foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

Kita hanya meminta beberapa field dalam tabel, karena itu hasilnya tidak dapat dianggap sebagai seluruh objek (lengkap), namun objek yg dikembalikan masih berupa resulset berjenis Phalcon \ MVC \ Model \ ResultSet \ Simple . Namun, setiap elemen adalah objek standar yang hanya berisi dua kolom yang diminta.

Nilai-nilai yang tidak mewakili objek lengkap kita sebut sbg skalar. PHQL memungkinkan Anda untuk query semua jenis skalar: kolom, fungsi, literal, ekspresi, dll.:

<?php

$phql = "SELECT CONCAT(c.id, ' ', c.name) AS id_name FROM Cars AS c ORDER BY c.name";
$cars = $manager->executeQuery($phql);
foreach ($cars as $car) {
    echo $car->id_name, "\n";
}

Sebagaimana kita dapat melakukan query berupa objek lengkap ataupun skalar, kita juga dapat melakukan query keduanya sekaligus:

<?php

$phql   = "SELECT c.price*0.16 AS taxes, c.* FROM Cars AS c ORDER BY c.name";
$result = $manager->executeQuery($phql);

Hasilnya kali ini adalah obyek Phalcon \ MVC \ Model \ ResultSet \ Complex . Hal ini memungkinkan akses ke kedua objek lengkap dan skalar sekaligus:

<?php

foreach ($result as $row) {
    echo "Name: ", $row->cars->name, "\n";
    echo "Price: ", $row->cars->price, "\n";
    echo "Taxes: ", $row->taxes, "\n";
}

Skalar dipetakan sebagai properti dari masing-masing “row”, sedangkan objek lengkap dipetakan sebagai properti dengan nama model terkait.

Join 

Sangat mudah untuk meminta record dari beberapa model menggunakan PHQL. Sebagian besar jenis Join didukung. Seperti kita mendefinisikan relasi dalam model, PHQL menambahkan kondisi ini secara otomatis:

<?php

$phql  = "SELECT Cars.name AS car_name, Brands.name AS brand_name FROM Cars JOIN Brands";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->car_name, "\n";
    echo $row->brand_name, "\n";
}

Secara default, diasumsikan sbg sebuah INNER JOIN . Anda dapat menentukan jenis JOIN dalam query:

<?php

$phql = "SELECT Cars.*, Brands.* FROM Cars INNER JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars LEFT JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars LEFT OUTER JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars CROSS JOIN Brands";
$rows = $manager->executeQuery($phql);

Juga dimungkinkan set manual kondisi JOIN:

<?php

$phql = "SELECT Cars.*, Brands.* FROM Cars INNER JOIN Brands ON Brands.id = Cars.brands_id";
$rows = $manager->executeQuery($phql);

Juga, join dapat dibuat dengan menggunakan beberapa tabel dalam klausa FROM:

<?php

$phql = "SELECT Cars.*, Brands.* FROM Cars, Brands WHERE Brands.id = Cars.brands_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo "Car: ", $row->cars->name, "\n";
    echo "Brand: ", $row->brands->name, "\n";
}

Jika alias digunakan untuk mengubah nama model dalam query, yang akan digunakan untuk nama atribut di setiap baris result:

<?php

$phql = "SELECT c.*, b.* FROM Cars c, Brands b WHERE b.id = c.brands_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo "Car: ", $row->c->name, "\n";
    echo "Brand: ", $row->b->name, "\n";
}

Ketika join model yg memiliki relasi many-to-many dengan model pada klausa ‘from’ , intermediate model yg menengahi secara implisit ditambahkan ke query yang dihasilkan:

<?php

$phql = 'SELECT Artists.name, Songs.name FROM Artists ' .
        'JOIN Songs WHERE Artists.genre = "Trip-Hop"';
$result = $this->modelsManager->query($phql);

Kode ini menghasilkan SQL berikut di MySQL:

SELECT `artists`.`name`, `songs`.`name` FROM `artists`
INNER JOIN `albums` ON `albums`.`artists_id` = `artists`.`id`
INNER JOIN `songs` ON `albums`.`songs_id` = `songs`.`id`
WHERE `artists`.`genre` = 'Trip-Hop'

Agregasi 

Contoh berikut ini menunjukkan bagaimana menggunakan agregasi dalam PHQL:

<?php

// How much are the prices of all the cars?
$phql = "SELECT SUM(price) AS summatory FROM Cars";
$row  = $manager->executeQuery($phql)->getFirst();
echo $row['summatory'];

// How many cars are by each brand?
$phql = "SELECT Cars.brand_id, COUNT(*) FROM Cars GROUP BY Cars.brand_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->brand_id, ' ', $row["1"], "\n";
}

// How many cars are by each brand?
$phql = "SELECT Brands.name, COUNT(*) FROM Cars JOIN Brands GROUP BY 1";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->name, ' ', $row["1"], "\n";
}

$phql = "SELECT MAX(price) AS maximum, MIN(price) AS minimum FROM Cars";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row["maximum"], ' ', $row["minimum"], "\n";
}

// Count distinct used brands
$phql = "SELECT COUNT(DISTINCT brand_id) AS brandId FROM Cars";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->brandId, "\n";
}

Kondisi 

Kondisi memungkinkan kita untuk menyaring set record kita ingin query. Klausa WHERE memungkinkan untuk melakukan itu:

<?php

// Simple conditions
$phql = "SELECT * FROM Cars WHERE Cars.name = 'Lamborghini Espada'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.price > 10000";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE TRIM(Cars.name) = 'Audi R8'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.name LIKE 'Ferrari%'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.name NOT LIKE 'Ferrari%'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.price IS NULL";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id IN (120, 121, 122)";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id NOT IN (430, 431)";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id BETWEEN 1 AND 100";
$cars = $manager->executeQuery($phql);

Dan juga, sebagai bagian dari PHQL, parameter yg telah disiapkan secara otomatis escape data input, memberikan keamanan lebih:

<?php

$phql = "SELECT * FROM Cars WHERE Cars.name = :name:";
$cars = $manager->executeQuery($phql, array("name" => 'Lamborghini Espada'));

$phql = "SELECT * FROM Cars WHERE Cars.name = ?0";
$cars = $manager->executeQuery($phql, array(0 => 'Lamborghini Espada'));

Insert Data 

PHQL bisa untuk memasukkan data menggunakan pernyataan INSERT:

<?php

// Inserting without columns
$phql = "INSERT INTO Cars VALUES (NULL, 'Lamborghini Espada', "
      . "7, 10000.00, 1969, 'Grand Tourer')";
$manager->executeQuery($phql);

// Specifying columns to insert
$phql = "INSERT INTO Cars (name, brand_id, year, style) "
      . "VALUES ('Lamborghini Espada', 7, 1969, 'Grand Tourer')";
$manager->executeQuery($phql);

// Inserting using placeholders
$phql = "INSERT INTO Cars (name, brand_id, year, style) "
      . "VALUES (:name:, :brand_id:, :year:, :style)";
$manager->executeQuery($sql,
    array(
        'name'     => 'Lamborghini Espada',
        'brand_id' => 7,
        'year'     => 1969,
        'style'    => 'Grand Tourer',
    )
);

Phalcon bukan hanya hanya mengubah pernyataan PHQL ke SQL. Semua event dan aturan bisnis yang ditetapkan dalam model dijalankan seolah-olah kita menciptakan objek individu model tsb secara manual. Mari menambahkan aturan bisnis di model mobil. Harga sebuah mobil tidak dapat kurang dari $ 10.000:

<?php

use Phalcon\Mvc\Model\Message;

class Cars extends Phalcon\Mvc\Model
{

    public function beforeCreate()
    {
        if ($this->price < 10000)
        {
            $this->appendMessage(new Message("A car cannot cost less than $ 10,000"));
            return false;
        }
    }

}

Jika kita membuat INSERT berikut dalam model Mobil, operasi tidak akan berhasil karena harga tidak memenuhi aturan bisnis yang kita terapkan:

<?php

$phql   = "INSERT INTO Cars VALUES (NULL, 'Nissan Versa', 7, 9999.00, 2012, 'Sedan')";
$result = $manager->executeQuery($phql);
if ($result->success() == false)
{
    foreach ($result->getMessages() as $message)
    {
        echo $message->getMessage();
    }
}

Update data 

Memperbarui baris sangat mirip daripada memasukkan baris. Seperti yang Anda ketahui, instruksi untuk memperbarui record adalah UPDATE. Ketika sebuah record diperbarui event yang berkaitan dengan operasi update akan dijalankan untuk setiap barisnya.

<?php

// Updating a single column
$phql = "UPDATE Cars SET price = 15000.00 WHERE id = 101";
$manager->executeQuery($phql);

// Updating multiples columns
$phql = "UPDATE Cars SET price = 15000.00, type = 'Sedan' WHERE id = 101";
$manager->executeQuery($phql);

// Updating multiples rows
$phql = "UPDATE Cars SET price = 7000.00, type = 'Sedan' WHERE brands_id > 5";
$manager->executeQuery($phql);

// Using placeholders
$phql = "UPDATE Cars SET price = ?0, type = ?1 WHERE brands_id > ?2";
$manager->executeQuery($phql, array(
    0 => 7000.00,
    1 => 'Sedan',
    2 => 5
));

Sebuah pernyataan UPDATE melakukan update dalam dua tahap:

  • Pertama, jika UPDATE memiliki klausa WHERE itu mengambil semua objek yang cocok dengan kriteria tersebut,
  • Kedua, berdasarkan objek hsail query makan akan melakukan update / perubahan atribut yg diminta menyimpannya ke database relasional

Dengan cara operasi ini memungkinkan untuk event, foreign key virtual dan validasi ikut andil dalam proses update. Singkatnya, kode berikut:

<?php

$phql = "UPDATE Cars SET price = 15000.00 WHERE id > 101";
$success = $manager->executeQuery($phql);

setara dengan:

<?php

$messages = null;

$process = function() use (&$messages) {
    foreach (Cars::find("id > 101") as $car) {
        $car->price = 15000;
        if ($car->save() == false) {
            $messages = $car->getMessages();
            return false;
        }
    }
    return true;
};

$success = $process();

Delete data 

Ketika sebuah record dihapus event yang berkaitan dengan operasi penghapusan akan dijalankan untuk setiap barisnya:

<?php

// Deleting a single row
$phql = "DELETE FROM Cars WHERE id = 101";
$manager->executeQuery($phql);

// Deleting multiple rows
$phql = "DELETE FROM Cars WHERE id > 100";
$manager->executeQuery($phql);

// Using placeholders
$phql = "DELETE FROM Cars WHERE id BETWEEN :initial: AND :final:";
$manager->executeQuery(
    $phql,
    array(
        'initial' => 1,
        'final' => 100
    )
);

Operasi DELETE  juga dilaksanakan dalam dua tahap seperti UPDATE.

Membuat query menggunakan Query Builder 

Sebuah Builder tersedia untuk membuat query PHQL tanpa perlu menulis pernyataan PHQL, juga menyediakan fasilitas IDE:

<?php

//Getting a whole set
$robots = $this->modelsManager->createBuilder()
    ->from('Robots')
    ->join('RobotsParts')
    ->orderBy('Robots.name')
    ->getQuery()
    ->execute();

//Getting the first row
$robots = $this->modelsManager->createBuilder()
    ->from('Robots')
    ->join('RobotsParts')
    ->orderBy('Robots.name')
    ->getQuery()
    ->getSingleResult();

Itu adalah sama dengan:

<?php

$phql = "SELECT Robots.*
    FROM Robots JOIN RobotsParts p
    ORDER BY Robots.name LIMIT 20";
$result = $manager->executeQuery($phql);

Contoh Builder yg lain:

<?php

// 'SELECT Robots.* FROM Robots';
$builder->from('Robots');

// 'SELECT Robots.*, RobotsParts.* FROM Robots, RobotsParts';
$builder->from(array('Robots', 'RobotsParts'));

// 'SELECT * FROM Robots';
$phql = $builder->columns('*')
                ->from('Robots');

// 'SELECT id FROM Robots';
$builder->columns('id')
        ->from('Robots');

// 'SELECT id, name FROM Robots';
$builder->columns(array('id', 'name'))
        ->from('Robots');

// 'SELECT Robots.* FROM Robots WHERE Robots.name = "Voltron"';
$builder->from('Robots')
        ->where('Robots.name = "Voltron"');

// 'SELECT Robots.* FROM Robots WHERE Robots.id = 100';
$builder->from('Robots')
        ->where(100);

// 'SELECT Robots.* FROM Robots WHERE Robots.type = "virtual" AND Robots.id > 50';
$builder->from('Robots')
        ->where('type = "virtual"')
        ->andWhere('id > 50');

// 'SELECT Robots.* FROM Robots WHERE Robots.type = "virtual" OR Robots.id > 50';
$builder->from('Robots')
        ->where('type = "virtual"')
        ->orWhere('id > 50');

// 'SELECT Robots.* FROM Robots GROUP BY Robots.name';
$builder->from('Robots')
        ->groupBy('Robots.name');

// 'SELECT Robots.* FROM Robots GROUP BY Robots.name, Robots.id';
$builder->from('Robots')
        ->groupBy(array('Robots.name', 'Robots.id'));

// 'SELECT Robots.name, SUM(Robots.price) FROM Robots GROUP BY Robots.name';
$builder->columns(array('Robots.name', 'SUM(Robots.price)'))
    ->from('Robots')
    ->groupBy('Robots.name');

// 'SELECT Robots.name, SUM(Robots.price) FROM Robots GROUP BY Robots.name HAVING SUM(Robots.price) > 1000';
$builder->columns(array('Robots.name', 'SUM(Robots.price)'))
    ->from('Robots')
    ->groupBy('Robots.name')
    ->having('SUM(Robots.price) > 1000');

// 'SELECT Robots.* FROM Robots JOIN RobotsParts';
$builder->from('Robots')
    ->join('RobotsParts');

// 'SELECT Robots.* FROM Robots JOIN RobotsParts AS p';
$builder->from('Robots')
    ->join('RobotsParts', null, 'p');

// 'SELECT Robots.* FROM Robots JOIN RobotsParts ON Robots.id = RobotsParts.robots_id AS p';
$builder->from('Robots')
    ->join('RobotsParts', 'Robots.id = RobotsParts.robots_id', 'p');

// 'SELECT Robots.* FROM Robots ;
// JOIN RobotsParts ON Robots.id = RobotsParts.robots_id AS p ;
// JOIN Parts ON Parts.id = RobotsParts.parts_id AS t';
$builder->from('Robots')
    ->join('RobotsParts', 'Robots.id = RobotsParts.robots_id', 'p')
    ->join('Parts', 'Parts.id = RobotsParts.parts_id', 't');

// 'SELECT r.* FROM Robots AS r';
$builder->addFrom('Robots', 'r');

// 'SELECT Robots.*, p.* FROM Robots, Parts AS p';
$builder->from('Robots')
    ->addFrom('Parts', 'p');

// 'SELECT r.*, p.* FROM Robots AS r, Parts AS p';
$builder->from(array('r' => 'Robots'))
        ->addFrom('Parts', 'p');

// 'SELECT r.*, p.* FROM Robots AS r, Parts AS p';
$builder->from(array('r' => 'Robots', 'p' => 'Parts'));

// 'SELECT Robots.* FROM Robots LIMIT 10';
$builder->from('Robots')
    ->limit(10);

// 'SELECT Robots.* FROM Robots LIMIT 10 OFFSET 5';
$builder->from('Robots')
        ->limit(10, 5);

// 'SELECT Robots.* FROM Robots WHERE id BETWEEN 1 AND 100';
$builder->from('Robots')
        ->betweenWhere('id', 1, 100);

// 'SELECT Robots.* FROM Robots WHERE id IN (1, 2, 3)';
$builder->from('Robots')
        ->inWhere('id', array(1, 2, 3));

// 'SELECT Robots.* FROM Robots WHERE id NOT IN (1, 2, 3)';
$builder->from('Robots')
        ->notInWhere('id', array(1, 2, 3));

// 'SELECT Robots.* FROM Robots WHERE name LIKE '%Art%';
$builder->from('Robots')
        ->where('name LIKE :name:', array('name' => '%' . $name . '%'));

// 'SELECT r.* FROM Store\Robots WHERE r.name LIKE '%Art%';
$builder->from(['r' => 'Store\Robots'])
        ->where('r.name LIKE :name:', array('name' => '%' . $name . '%'));

Bound Parameter 

Bound Parameter dalam query builder dapat ditetapkan ketika query dibangun atau diberikan sekaligus ketika eksekusi:

<?php

//Passing parameters in the query construction
$robots = $this->modelsManager->createBuilder()
    ->from('Robots')
    ->where('name = :name:', array('name' => $name))
    ->andWhere('type = :type:', array('type' => $type))
    ->getQuery()
    ->execute();

//Passing parameters in query execution
$robots = $this->modelsManager->createBuilder()
    ->from('Robots')
    ->where('name = :name:')
    ->andWhere('type = :type:')
    ->getQuery()
    ->execute(array('name' => $name, 'type' => $type));

Pelarangan literal di PHQL 

Penggunaan variabel secara Literal dapat dinonaktifkan dalam PHQL, ini berarti bahwa menggunakan string secara langsung, angka dan nilai-nilai boolean di PHQL string akan dianulir. Jika pernyataan PHQL diciptakan dgn embed data eksternal padanya, ini bisa membuka potensi injeksi SQL:

<?php

$login = 'voltron';
$phql = "SELECT * FROM Models\Users WHERE login = '$login'";
$result = $manager->executeQuery($phql);

Jika $login berubah menjadi ‘OR” =’, yang diproduksi PHQL adalah:

<?php

"SELECT * FROM Models\Users WHERE login = '' OR '' = ''"

Yang selalu true tidak peduli data login apa yg tersimpan dalam database.

Jika literal dianulir dan pernyataan PHQL seperti diatas digunakan, maka akan mengeluarkanexception, memaksa pengembang untuk menggunakan bound parameter. Query yang sama dapat ditulis dalam cara yang aman seperti ini:

<?php

$phql = "SELECT Robots.* FROM Robots WHERE Robots.name = :name:";
$result = $manager->executeQuery($phql, array('name' => $name));

Anda dapat memberi batasan literal dengan cara berikut:

<?php

Phalcon\Mvc\Model::setup(array('phqlLiterals' => false));

Bound Parameter dapat digunakan walau literal diperbolehkan atau tidak. Pelarangan tsb hanyalah tambahan opsi keputusan keamanan dalam aplikasi web.

Escape Reserved Word 

PHQL memiliki beberapa Reserved Word yg tak boleh digunakan, jika Anda ingin menggunakan salah satu dari mereka sebagai nama atribut atau model , Anda perlu untuk escape kata-kata tsb dengan menggunakan pembatas escape lintas-database ‘[‘ dan ‘]’:

<?php

$phql = "SELECT * FROM [Update]";
$result = $manager->executeQuery($phql);

$phql = "SELECT id, [Like] FROM Posts";
$result = $manager->executeQuery($phql);

Pembatas secara dinamis diterjemahkan ke pembatas yg berlaku pada sistem database dimana aplikasi sedang berjalan.

Phalcon Query Language Lifecycle 

Sebagai bahasa tingkat tinggi, PHQL memberikan pengembang kemampuan untuk mempersonalisasikan dan menyesuaikan berbagai aspek dalam rangka untuk memenuhi kebutuhan masing-masing. Berikut ini adalah siklus hidup/ Lifecycle dari setiap pernyataan PHQL yg dieksekusi:

  • PHQL ini diurai dan diubah menjadi Intermediate Representation (IR) yang independen/terlepas dari SQL dr sistem database yg sedang aktif digunakan
  • IR tersebut dikonversi ke SQL yang valid sesuai dengan sistem database yang terkait dengan model
  • Pernyataan PHQL diurai sekali dan di-cache dalam memori. Eksekusi berikutnya dari pernyataan yang sama menghasilkan eksekusi yg sedikit lebih cepat

Menggunakan Raw SQL 

Sebuah sistem database dapat menawarkan ekstensi SQL tertentu yang tidak didukung oleh PHQL, dalam hal ini, Raw-SQL yg benar-benar sesuai dgn dialek SQL database yg sedang aktif digunakan akan lebih tepat:

<?php

use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

class Robots extends Phalcon\Mvc\Model
{
    public static function findByCreateInterval()
    {
        // A raw SQL statement
        $sql = "SELECT * FROM robots WHERE id > 0";

        // Base model
        $robot = new Robots();

        // Execute the query
        return new Resultset(null, $robot, $robot->getReadConnection()->query($sql));
    }
}

Jika query raw-SQL sering digunakan dalam aplikasi Anda, metode generik dapat ditambahkan ke model Anda:

<?php

use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

class Robots extends Phalcon\Mvc\Model
{
    public static function findByRawSql($conditions, $params=null)
    {
        // A raw SQL statement
        $sql = "SELECT * FROM robots WHERE $conditions";

        // Base model
        $robot = new Robots();

        // Execute the query
        return new Resultset(null, $robot, $robot->getReadConnection()->query($sql, $params));
    }
}

The findByRawSql di atas dapat digunakan sebagai berikut:

<?php

$robots = Robots::findByRawSql('id > ?', array(10));

Troubleshooting 

Beberapa hal yang perlu diingat ketika menggunakan PHQL:

  • Class bersifat case-sensitive, jika class tidak didefinisikan dengan nama yang sama seperti yang dibuat ini dapat menyebabkan perilaku tak terduga dalam sistem operasi dengan file-sistem case-sensitif seperti Linux.
  • Charset yang benar harus didefinisikan dalam koneksi agar bind parameter berjalan
  • Class dgn alias tidak diganti oleh class dgn full-namespace karena ini hanya terjadi dalam kode PHP dan tidak di dalam string
  • Jika penggantian nama kolom diaktifkan, hindari menggunakan alias kolom dengan nama yang sama dengan nama kolom yang asli, ini mungkin membingungkan resolver permintaan
 
Terjemahan dari Phalcon Query Language (PHQL)
http://docs.phalconphp.com/en/latest/reference/phql.html