Overview

Packages

  • tipy

Classes

  • Tipy
  • TipyApp
  • TipyCli
  • TipyCliSession
  • TipyConfig
  • TipyController
  • TipyCookie
  • TipyDAO
  • TipyEnv
  • TipyFlash
  • TipyInflector
  • TipyInput
  • TipyIOWrapper
  • TipyLogger
  • TipyMailer
  • TipyModel
  • TipyOutput
  • TipyRequest
  • TipySession
  • TipyTestCase
  • TipyTestRunner
  • TipyView

Exceptions

  • AssertionFailedException
  • CompileErrorException
  • CompileWarningException
  • CoreErrorException
  • CoreWarningException
  • DeprecatedException
  • NoMethodException
  • NoticeException
  • ParseException
  • RecoverableErrorException
  • StrictException
  • TipyDaoException
  • TipyException
  • TipyModelException
  • TipyRollbackException
  • TipyValidationException
  • UserDeprecatedException
  • UserErrorException
  • UserNoticeException
  • UserWarningException
  • WarningException
  • Overview
  • Package
  • Class
  • Deprecated
  • Todo

Class TipyModel

M in MVC. TipyModel is ORM connecting objects to database

TipyModel:

  • Represents row in a table
  • Defines associations and hierarchies between models
  • Validates models before they get persisted to the database
  • Performs database operations in an object-oriented fashion

Conventions

TipyModel follows "Convention over Configuration" paradigm and tries to use as little configuration as possible. Of course you can configure model-database mapping in the way you wish but you will write your code much faster following TipyModel conventions:

  • Class Name - singular, CamelCase, first letter in upper case. - BlogPost
  • Table Name - plural, snake_case, all letters in lower case - blog_posts
  • Model Properties - camelCase, first letter in lower case - createdAt
  • Table Fields - snake_case, all letters in lower case - created_at
  • Foreign Key - foreign table name + "_id" - author_id
  • Primary Key - is always id
  • If your table has created_at and updated_at fields they will be handled automatically

These conventions can be changed by overriding TipyModel methods: TipyModel::classNameToTableName(), TipyModel::fieldNameToAttrName(), TipyModel::tableForeignKeyFieldName(), TipyModel::classForeignKeyAttr(), and constants: TipyModel::CREATED_AT, TipyModel::UPDATED_AT

Defining Models

Let's say you have the following table

create table users (
    id int(11),
    first_name varchar(255),
    last_name varchar(255),
    primary key (id)
);

To make TipyModel from this table you simply need to extend TipyModel class

class User extends TipyModel {
}

This magically connect User class to users table (see Conventions section above) gives User class a lot of useful methods and magic properties to access table fields

// create new User object and save it to database
$user = new User();
$user->firstName = 'John';
$user->lastName = 'Doe';
$user->save();

// or like this
$user = User::create([
    'firstName' => 'John',
    'lastName'  => 'Doe'
]);

$id = $user->id;
$sameUser = User::load($id);
echo $sameUser->firstName;

Validation

Model-level validation is the best way to ensure that only valid data is saved into database. It cannot be bypassed by end users and is convenient to test and maintain.

Validations are run autmatically before TipyModel::save() and TipyModel::update() send SQL INSERT or UPDATE queries to the database.

To add validation to your model simply override TipyModel::validate() method.

class User extends TipyModel {
   public function validate() {
       if (!$this->firstName) throw new TipyValidtionException('First name should not be blank!');
       if (!$this->lastName) throw new TipyValidtionException('Last name should not be blank!');
   }
}

The common way to fail validation is to throw TipyValidtionException and then to catch it in controller.

Hooks

TipyModel allows to define logic triggered before or after an alteration of the model state. To do this override the following methods in your model:

  • TipyModel::beforeCreate()
  • TipyModel::afterCreate()
  • TipyModel::beforeUpdate()
  • TipyModel::afterUpdate()
  • TipyModel::beforeDelete()
  • TipyModel::afterDelete()

Associations

Association is a connection between two models. By declaring associations you define Primary Key-Foreign Key connection between instances of the two models, and you also get a number of utility methods added to your model. Tipy supports the following types of associations:

  • TipyModel::$hasMany
  • TipyModel::$hasOne
  • TipyModel::$belongsTo
  • TipyModel::$hasManyThrough

To define define model associations you need to assign values to these properties.

class User extends TipyModel {
   protected $hasMany = ['posts', 'comments'];
}

Association class name is evaluated automatically by TipyInflector::classify() inflection. If you wan't to specify class name different from association name you can pass association options as arrays:

class User extends TipyModel {
   protected $hasMany = [
       'posts' => ['class' => 'BlogPost'],
       'comments' => ['class' => 'BlogComment']
   ];
}

hasMany

A hasMany association indicates a one-to-many connection with another model.

create table users (               create table blog_posts (
    id int(11), <---------------+      id int(11),
    first_name varchar(255),    |      title varchar(255),
    last_name varchar(255),     |      body text,
    primary key (id)            +----- user_id int(11),
);                                     primary key (id)
                                   );
class User extends TipyModel {
   protected $hasMany = [
       'posts' => ['class' => 'BlogPost']
   );
}

This gives User model magic property User::posts

$posts = $user->posts;

hasOne

A hasOne association indicates a one-to-one connection with another model.

create table users (               create table accounts (
    id int(11), <---------------+      id int(11),
    first_name varchar(255),    |      cc_number varchar(20),
    last_name varchar(255),     |      cc_expire_date varchar(5),
    primary key (id)            +----- user_id int(11),
);                                     primary key (id)
                                   );
class User extends TipyModel {
   protected $hasOne = ['account'];
}

This gives User model magic property User::account

$ccNumber = $user->account->ccNumber;

belongsTo

A belongsTo association is an opposite to hasMany and hasOne

create table users (               create table blog_posts (
    id int(11), <---------------+      id int(11),
    first_name varchar(255),    |      title varchar(255),
    last_name varchar(255),     |      body text,
    primary key (id)            +----- user_id int(11),
);                                     primary key (id)
                                   );
class BlogPost extends TipyModel {
   protected $belongsTo = ['user'];
}

This gives BlogPost model magic property BlogPost->user

$firstName = $post->user->firstName;

hasManyThrough

A hasManyThrough association indicates a many-to-many connection with another model through a third model.

create table users (
    id int(11), <-------------+
    first_name varchar(255),  |   create table memberships (
    last_name varchar(255)    +------ user_id int(11).
);                                    id int(11),
                              +------ group_id int(11)
create table groups (         |   );
    id int(11), <-------------+
    name varchar(255)
);
class User extends TipyModel {
   protected $hasManyThrough = [
       'groups' => ['class' => 'Group', 'through' => 'Membership'],
   );
}

This gives User model magic property User::groups

$groups = $user->groups;

Association Options

Association options are arrays with the following keys

  • 'class' - associated model class
  • 'dependent' - 'delete', 'nullify', or null - What to do with associated model rows when this model row is deleted
  • 'conditions' - conditions to select associated records in addition to foreign_key.
  • 'values' - values for conditions
  • 'foreign_key' - custom associated record foreign key
  • 'through_key' - custom second foreign key for $hasManyThrough
class User extends TipyModel {
   protected $hasMany = [
       'messages' => [
           'class' => 'Message',
           'dependent' => 'delete'
       ],
       // 7 days old messages
       'oldMessages' => [
           'class' => 'Message',
           'conditions' => 'created_at < ?',
           'values' => [strtotime('-1 week')]
       ]
   ];
}

Associations Cache

Associations are cached. So if you call

$post->comments

more than once only one query will be executed (first call) and then comments will always be taken from cache.

Downside of this approach: Cache doesn't know if comments were deleted or modified in the database. To reset associations cache use TipyModel::reload()

Associations with conditions are not cached. This means that

$post->comments(['order' => 'created_at desc'])

will allways execute query

In short always look for parethesis:

$post->comments; // is cached
$post->comments(...); // is not cached
TipyDAO
Extended by TipyModel
Package: tipy
Todo: Accept conditions and values in one array 'conditions' => ['id = ?', $id]
Todo: Improve find() to accept arguments like Post::find('first', 'conditions' => ['created_at > ?', time()]);
Located at src/TipyModel.php
Methods summary
public
# __construct( array $attrs = null )

Create new model

Create new model

$post = new BloPost;

$post = new BloPost([
    'title' => 'Hello',
    'body' => 'World!'
]);

NOTE: This method does not save anything to database

  • New model is not saved to the database and has $this->isNewRecord() == true
  • If $attrs has 'id' key then model with the same id will be loaded from the database and its attributes will be overwritten (but not saved)

Parameters

$attrs
Associative array representing new model properies

Overrides

TipyDAO::__construct()
public
# __toString( )

Output something on echo $obj;

Output something on echo $obj;

public
# __set( string $name, mixed $value )

Magic method to assign model property

Magic method to assign model property

Parameters

$name
$value

Throws

TipyModelException
if model property is not defined
public
# __get( string $name )

Magic method to get the model property value or to execute model method

Magic method to get the model property value or to execute model method

Lookup order:

  • model methods
  • model assosications
  • model properties

Parameters

$name

Throws

TipyModelException
on lookup failure
public
# __call( string $name, array $args )

Magic method to call model property as a function

Magic method to call model property as a function

Use it to pass conditions to assiciation properties

$post->comments(['conditions' => 'created_at > ?', 'values' => strtotime('-1 day')])

Parameters

$name
$args

Throws

TipyModelException
on lookup failure
public
# checkAttribute( $name )

Check for model property existance

Check for model property existance

Throws

TipyModelException
if property does not exist
protected
# makeReflection( )

Reflect database table to model

Reflect database table to model

Loads table columns and creates model properties from them

public static TipyModel|null
# load( integer $id )

Creale model instance from the table row

Creale model instance from the table row

$post = BlogPost::load(123);

Parameters

$id

Returns

TipyModel|null
public boolean
# isNewRecord( )

True if new model instance has not been saved yet

True if new model instance has not been saved yet

Returns

boolean
public mysqli_result
# save( )

Save model data as a table row

Save model data as a table row

Returns

mysqli_result

Throws

TipyModelException
if model is marked as deleted
public
# update( string $name, mixed $value )

Set model property and update model row immediately

Set model property and update model row immediately

Parameters

$name
$value

Throws

TipyModelException
if property does not exist
public
# reload( )

Reload model from the database

Reload model from the database

Throws

TipyModelException
if trying to reload new (not-saved) model
TipyModelException
if trying to reload model marked as deleted
public static TipyModel
# create( array $attr = null )

Create new model, save it to the database, and return model instance

Create new model, save it to the database, and return model instance

$post = BlogPost::create([
    'title' => 'Hello World'
])

// is equivalent of

$post = new BlogPost;
$post->title = 'Hello World';
$post->save();

Parameters

$attr
$attrs

Returns

TipyModel
protected mysqli_result
# createNewRecord( )

Save new model as a table row

Save new model as a table row

Returns

mysqli_result
protected mysqli_result
# updateRecord( )

Updates table row connected to model

Updates table row connected to model

Returns

mysqli_result
public mysqli_result
# delete( )

Delete table row connected to model

Delete table row connected to model

  • deletes table row
  • deletes all rows for associations with 'dependent' => 'delete'
  • set to null all foreign keys for associations with 'dependent' => 'nullify'
  • marks model as deleted

Returns

mysqli_result
public static integer
# count( $options = [] )

Count model records by conditions

Count model records by conditions

$post = BlogPost::count([
     'conditions' => "user_id = ?",
     'values' => [42]
]);

Returns

integer
public static array
# find( array $options = ['values' => []] )

Return array of models by conditions

Return array of models by conditions

$post = BlogPost::find([
     'conditions' => "title =?",
     'values' => ['Hello'],
     'limit' => 2.
     'offset' => 3,
     'order' => 'user_id asc'
]);

Parameters

$options

Returns

array
public static TipyModel
# findFirst( $options = [] )

Same as find but return only first instance

Same as find but return only first instance

$post = BlogPost::findFirst([
     'conditions' => "title =?",
     'values' => ['Hello'],
     'limit' => 2.
     'offset' => 3,
     'order' => 'user_id asc'
]);

Returns

TipyModel
public
# validate( )

Validates model data

Validates model data

Executed before SQL INSERT or UPDATE statements are sent to database. Override this in your model classes

protected static TipyModel
# instanceFromResult( TipyModel $instance, mysqli_result $result )

Fill model instance with data from mysqli_result

Fill model instance with data from mysqli_result

Parameters

$instance
$result

Returns

TipyModel
protected mixed
# typeCast( string $field, mixed $value )

Cast column value to PHP type

Cast column value to PHP type

MySQL returns all field values as strings. This method return value casted to PHP type using rules from TipyModel::$mysqlToPhpTypes

Parameters

$field
field
$value

Returns

mixed
public
# lockForUpdate( )

Lock model's connected row for update

Lock model's connected row for update

Should be called inside TipyDAO::transaction()

Throws

TipyDaoException
if called outside transaction
public
# beforeCreate( )

TipyModel hook called before TipyModel::create() and TipyModel::createNewRecord()

TipyModel hook called before TipyModel::create() and TipyModel::createNewRecord()

Override this in your model to add logic

public
# afterCreate( )

TipyModel hook called after TipyModel::update() and TipyModel::updateRecord()

TipyModel hook called after TipyModel::update() and TipyModel::updateRecord()

Override this in your model to add logic

public
# beforeUpdate( )

TipyModel hook called before TipyModel::update() and TipyModel::createNewRecord()

TipyModel hook called before TipyModel::update() and TipyModel::createNewRecord()

Override this in your model to add logic

public
# afterUpdate( )

TipyModel hook called after TipyModel::update() and TipyModel::createNewRecord()

TipyModel hook called after TipyModel::update() and TipyModel::createNewRecord()

Override this in your model to add logic

public
# beforeDelete( )

TipyModel hook called before TipyModel::delete()

TipyModel hook called before TipyModel::delete()

Override this in your model to add logic

public
# afterDelete( )

TipyModel hook called after TipyModel::delete()

TipyModel hook called after TipyModel::delete()

Override this in your model to add logic

protected static string
# classNameToTableName( string $className )

Converts table name to model class name

Converts table name to model class name

You may override this method to change the rules

Parameters

$className

Returns

string
protected static string
# fieldNameToAttrName( string $fieldName )

Converts table column name to model property name

Converts table column name to model property name

You may override this method to change the rules

Parameters

$fieldName

Returns

string
protected static string
# tableForeignKeyFieldName( string $tableName )

Return foreign key name for foreign table

Return foreign key name for foreign table

You may override this method to change the rules

Parameters

$tableName

Returns

string
protected static string
# classForeignKeyAttr( string $className )

Associated model property representing foreign key

Associated model property representing foreign key

You may override this method to change the rules

Parameters

$className

Returns

string
protected static integer
# getCurrentTime( )

Return current time for created_at and updated_at columns

Return current time for created_at and updated_at columns

Default TipyModel convention is to use unix timestamp for timestamps. You can change this by overriding this method in your model

Returns

integer
Methods inherited from TipyDAO
affectedRows(), fetchAllRows(), fetchRow(), isTransactionInProgress(), lastInsertId(), limitQuery(), limitQueryAllRows(), numRows(), query(), queryAllRows(), queryRow(), rollback(), transaction()
Constants summary
string CREATED_AT
# 'created_at'
string UPDATED_AT
# 'updated_at'
Properties summary
protected static array $mysqlToPhpTypes

Rules to cast MySQL types to PHP types

Rules to cast MySQL types to PHP types

# [ 'char' => 'string', 'varchar' => 'string', 'binary' => 'string', 'varbinary' => 'string', 'blob' => 'string', 'text' => 'string', 'enum' => 'string', 'set' => 'string', 'integer' => 'integer', 'int' => 'integer', 'smallint' => 'integer', 'tinyint' => 'integer', 'mediumint' => 'integer', 'bigint' => 'integer', 'bit' => 'integer', 'boolean' => 'integer', 'decimal' => 'float', 'numeric' => 'float', 'double' => 'float', 'date' => 'datetime', 'datetime' => 'datetime', 'timestamp' => 'datetime' ]
protected static array $globalReflections

Global models<->tables reflections cache

Global models<->tables reflections cache

One for all models

# []
public string $className

Model class name

Model class name

Just to call get_class only once

#
public string $table

Table name

Table name

#
public array $attributes

Magic properties to access row columns

Magic properties to access row columns

filled automatically

#
public array $fields

Table column names list

Table column names list

fields autmatically

#
public array $fieldTypes

Table column types list

Table column types list

filled autmatically

#
public array $reflections

column => property reflections

column => property reflections

Associative array with connections betweed table fields and model's magic propertiesa

#
public array $data

Magic properties values

Magic properties values

#
public boolean $isDeletedRecord

True if table row represented by model is deleted

True if table row represented by model is deleted

#
public array $associationsCache

Associations cache

Associations cache

#
protected array $hasMany

One-to-many associations

One-to-many associations

#
protected array $hasOne

One-to-one associations

One-to-one associations

#
protected array $belongsTo

Many-to-one associations

Many-to-one associations

#
protected array $hasManyThrough

Many-to-many associations through a third models

Many-to-many associations through a third models

#
Properties inherited from TipyDAO
$dbLink, $logger
tipy API documentation generated by ApiGen