Welcome to FrostSW!

FrostMVC PHP Framework

Documentation   |   Version
FrostMVC / app / models

models/

A model represents one database table. It extends FrostMVC's Model class, which gives it a query builder: you chain methods to build the SQL, and every value is bound as a parameter, so user input never ends up inside the SQL text.

Set your database name, server, username and password at the bottom of app/settings.php. See Configuration.

Creating a model

 app/models/Posts.php
<?php

namespace MyApp\models;

use FrostMVC\Model;

class Posts extends Model {

    public function published() {
        return $this->get()->where('status', 'publish')->order('created', 'DESC')->read();
    }
}

The table name comes from the class name: TABLE_PREFIX + the lowercase class name + TABLE_SUFFIX. With the default TABLE_SUFFIX of _tbl, Posts uses the table posts_tbl. Call $this->setTableName('other_table') in the constructor to use a different table.

Reading

use MyApp\models\Posts;

$posts  = (new Posts)->get()->read();                                   // all rows
$post   = (new Posts)->get()->where('id', $id)->readRow();              // one row, [] when none
$title  = (new Posts)->get('title')->where('id', $id)->readScalar();    // one value
$recent = (new Posts)->get(['id', 'title'])
                     ->where('status', 'publish')
                     ->and_('views', '>=', 100)
                     ->order('created', 'DESC')
                     ->limit(0, 10)                                     // offset, count
                     ->read();
get($cols)Start a SELECT. Omit $cols for all columns.
where('col', $value)
where(['a' => 1, 'b' => 2])
Equality conditions (an array is AND-joined).
and_('col', '>=', $value)
or_('col', $value)
Add more conditions, optionally with an operator.
in([...]), like($pattern), between($a, $b), not(...)More comparisons, chained after a column.
order('col', 'DESC'), group('col'), limit($count)Sorting, grouping and paging.
join('Authors', Model::escape('authors_tbl.id = posts_tbl.author'))Join another model's table, with the full ON condition. Pass 'LEFT' as the third argument for a LEFT JOIN.
read() / readRow() / readScalar()Run it: all rows, the first row, or the first column of the first row.

Writing

$id = (new Posts)->insert(['title' => $title, 'status' => 'draft'])->execute();   // new row ID

(new Posts)->update(['status' => 'publish'])->where('id', $id)->execute();        // true
(new Posts)->update('views', Model::escape('views + 1'))->where('id', $id)->execute();

(new Posts)->delete()->where('id', $id)->execute();

execute() returns the new ID for an INSERT, true for UPDATE and DELETE, and false when the query fails. Always chain where() on updates and deletes.

Model::escape() marks a value as raw SQL, so it is not bound as a parameter. Only use it for SQL you wrote yourself, never for user input.

Caching results

Chain cache() before a read to keep the result for TABLE_CACHE_EXPIRATION seconds (600 by default). When the cached copy is stale it is still returned immediately, and the query is refreshed in the background after the page is sent:

$menu = (new Categories)->get()->order('name')->cache()->read();

Raw queries

For SQL the builder does not cover, pass it to execute(). Use named placeholders and pass their values by name, so they are bound as parameters:

$rows = (new Posts)->execute(
    'SELECT author, COUNT(*) AS total FROM posts_tbl WHERE created >= :since GROUP BY author',
    ['since' => $since],
    true        // return rows
);

Using a model from a controller

use MyApp\models\Posts;

public function index() {
    View::get('blog/index', ['posts' => (new Posts)->published()]);
}

Next: Views & Twig · Reference: Model, DB