PHPackages                             silverengine/reflectorm - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. silverengine/reflectorm

ActiveProject[Utility &amp; Helpers](/categories/utility)

silverengine/reflectorm
=======================

1.0.0(6y ago)24MITPHPPHP &gt;=7.2

Since Sep 24Pushed 6y ago5 watchersCompare

[ Source](https://github.com/SilverEngine/ReflectORM)[ Packagist](https://packagist.org/packages/silverengine/reflectorm)[ RSS](/packages/silverengine-reflectorm/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (1)DependenciesVersions (2)Used By (0)

Query class
===========

[](#query-class)

About Query
===========

[](#about-query)

Query is class which helps us to build SQL query and get result from database.

With using Query class we have next advantages:

1. All values are properly escaped.
2. Same query can be executed on different databases.
3. Easy use of transactions and sub-transactions (aka savepoints)
4. …

Building quries
===============

[](#building-quries)

```
Query::select('column')
    ->from('table')
    ->where('column2', '=', 'value')
    ->orderBy('column3', 'asc')
    ->limit(2);
```

This is an example query which, give us following result:

```
SELECT `column` FROM `table` WHERE `column2` = ? ORDER BY `column3` ASC LIMIT 2;
-- values: ['value']
```

First when we want to create query, we call one of next functions which give use specialized Query for our statement:

 ::select()[Select](System/Database/Query/Select.php) ::delete()[Delete](System/Database/Query/Delete.php) ::update()[Update](System/Database/Query/Update.php) ::insert()[Insert](System/Database/Query/Insert.php) ::drop()[Drop](System/Database/Query/Drop.php) ::create()[Create](System/Database/Query/Create.php) ::alter()[Alter](System/Database/Query/Alter.php)When we get query, we methods of this query. Because more queries uses same methods, they are stored separated and included as Traits.

Query methods
-------------

[](#query-methods)

Each provided method accept values which are known as Query parts. Query part type is written before variable name (only in this documentation).

What can we pass for Query part is described in [Query parts]() chapter.

### From

[](#from)

 from($table, $alias = null, Column $id1 = null, Column $id2 = null)From which table we select data.

```
$q->from('table');
$q->from('table', 't');
$q->from('table t');
$q->from('table2', 't2', 't1.id', 't2.id_table1');
$q->from('table2 t2', 't1.id', 't2.id_table1');
$q->from('table2', 't1.id', 'table2.id_table1');
```

We can pass subquery too:

```
$q->from(Query::select()->from('other'), 'alias_must_exists');
// or
$q->from(function() {
    return Query::select()->from('tbl')->whereActive(true);
}, 'alias');
```

### Join

[](#join)

 join(Table $table, JoinCondition $condition)Inner join table leftJoin(Table $table, JoinCondition $condition)Left join table rightJoin(Table $table, JoinCondition $condition)Right join table```
$q->join('table', ['table.id', 'other_table.id']);
```

### Where and Having

[](#where-and-having)

- where(Column $column, Raw $operator, Value $value, string $how = ‘and’, boolean $not = false)
- having(Column $column, Raw $operator, Value $value, string $how = ‘and’, boolean $not = false)

```
$q->where('column', '=', 'value');
$q->where('column', 'between', [10, 20], 'or', true); // or not between 10-20
```

We can skip operator and write value instead (but in this case, we cant modify $how and $not values)

```
$q->where('column', 'value'); // column = value
```

\_\_call() function simplify where call for us. We can concatenate more words for method name to get what we want: (Order is important!)

 orif we want connect this condition with OR (optional) notif we want negate this condition (optional) where / havingone of them column nameColumn name in camelCase will be transformed into snake\_case (optional)When we call this special method, we need to pass folowing arguments:

- column (if it is not passed yet)
- operator (required)
- value (required)

```
$q->whereId(1); // operator is skipped
$q->whereId('=', 1); // same thing
$q->whereAge('between', [10, 20]); // AND age between 10 and 20
$q->notWhereAge('between', [10, 20]); // AND NOT age between 10 and 20
$q->orNotWhereAge('between', [10, 20]); // OR NOT age between 10 and 20
$q->orWhere('specialColumn', 3); // AND specialColumn = 3
```

#### Operators

[](#operators)

Default operators are:

 INIf value is array ISIf value is null =For all other valuesSpecial operators:

 BETWEENit needs an array of two values for valueAll other operators are transformed to upper case. Operator is not escaped or treated specially. It is concatenated to SQL statement like raw value. We can write into whatever we want (spaces and comment too… please don’t do any stupid)

#### Special use of where - parentheses

[](#special-use-of-where---parentheses)

If we want determine operators priority, we can use where() function to put conditions in parentheses.

```
// WHERE active = true AND age = 10 OR age = 11
$q->whereActive(true)
    ->whereAge(10)
    ->orWhereAge(11);

// WHERE active = true AND (age = 10 OR age = 11)
$q->whereActive(true)
    ->where(function ($q) {
        $q->whereAge(10)
            ->orWhereAge(11);
    });

// High order function example
function find_best($sex) { return function($q) use ($sex) { /* ... */ } }
$q->where(find_best('female'));
```

In this example, all where conditions (not having) is putted into parens. In inner function we still can do whatever we want with query (order by, having, limit, …), if we really want this…

### GroupBy

[](#groupby)

If we use having from previous chapter, we need first group some data.

- groupBy(Column $column)

This is it. If we want group more columns, we can call groupBy() more times.

### Order

[](#order)

- orderBy(Name $column, $dir = ‘asc’)

```
$q->orderBy('model')
    ->orderBy('year', 'desc');

// ORDER BY model asc, year desc
```

### Limit

[](#limit)

 limit(int $count, $offset = null)Standard limit offset(int $offset)If limit is not specified yet it will be set to 1 page(int $page, int $per\_page = null)First argument is page number (starting with 1), second argument is page size.```
$q->limit(4); // LIMIT 4
$q->limit(4, 6); // LIMIT 4 OFFSET 5
$q->offset(3); // LIMIT 1 OFFSET 3
// ^ Actually limit should be 4, becouse we set it in second row

/* Page size = 10
 *
 * Page:
 * 1. 0-9
 * 2. 10-19
 * 3. 20-29
 */
$q->page(3, 10); // LIMIT 10 OFFSET 20
```

### Union

[](#union)

- union(Query $q)
- unionAll(Query $q)

```
$q->union(Query::select()->from('table')->limit(1));

// If we are more comfortable with callback, we can use it:
$q->union(function() {
    return Query::select()
        ->from('table')
        ->limit(1);
});
```

Query parts
-----------

[](#query-parts)

Query parts are stored in [System/Database/Parts/](System/Database/Parts/). We can construct them with $part = OutPart::ensure(\[‘construct’, ‘values’\]); or via constructor $part = new OurPart(‘construct’, ‘values’);

If Part is defined before Query method parameter (in this doc), value will be passed through Part::ensure($value) function. If value is already part (maybe Raw), ensure will not touch it, otherwise value will be constructed into declared Part.

```
Column::ensure('col'); /* is same as */ Column::ensure(['col']);

$q->where('col', '=', 24);

/*
 * So, because where is declared as: where(Column $c, $op, Value $v)
 * 'col' will be transformed with Column::ensure('col')
 * and 24 with Value::ensure(24);
 */

// If we want create Column like: new Column('table', 'col', 'alias');
$q->where(['table', 'col'], ...);

// becouse we can construct Column like: new Column('table.col alias'):
$q->where('table col');

// If we pass our own part into where, Column::ensure will skip it
$q->where(new Raw('COUNT(*)'), 5)
```

We can pass alias into where column too, but we don’t want to.

### Raw

[](#raw)

Arguments: (string $raw\_value)

In raw we pass raw sql.

```
$q->select(new Raw('COUNT(*) count'))->from('table');
```

### Literal

[](#literal)

Arguments: (mixed $value)

Literal ensure that passed value will be properly quoted. Following methods are predefined:

 Literal::wild()new Raw(‘\*’) Literal::null()new Literal(null) Literal::true()new Literal(true) Literal::false()new Literal(false)We can pass into literal any value, and it will appear in sql statement:

```
$q->select(Literal::wild())
    ->where('active', Literal::true())
    ->notWhereLastLogin(Literal::null())
    ->where('string', new Literal('this is string'))
    ->where('age', 'IN', new Literal([20, 21, 22])); // We must write
                                                     // IN operator,
                                                     // becouse
                                                     // Literal is not
                                                     // recognized as
                                                     // array
// SELECT * FROM ? WHERE active = true AND NOT WHERE last_login = null AND string = 'this is string' AND age IN [20, 21, 22]
// We should specify IS operator for NULL too!
```

If we doesn’t use Literal, Query will transform values into Value part, which uses placeholders.

```
$q->select(Literal::wild())
    ->where('active', true)
    ->notWhereLastLogin(null)
    ->where('string', 'this is string')
    ->where('age', [20, 21, 22]);
// SELECT * FROM ? WHERE active = ? AND NOT WHERE last_login IS ? AND string = ? AND age IN [?, ?, ?]
```

### Value

[](#value)

Arguments: (mixed $value)

All values passed into Query are transformed into Value. Value is stored into Query bindings and ‘?’ placeholder is inserted into SQL statement.

### Fn

[](#fn)

Arguments: (string $name, …$args)

Function call:

 Fn::count(Column $column = null)(default is Literal::wild()) Fn::groupConcat(Column $column, string $sep = ‘,’)GROUP\_CONCAT functionOther function call can be constructed via special \_\_call() method like:

 Fn::myOwnFunction(1,2,3)myOwnFunction(1,2,3); Fn::THIS\_IS\_FN(1,’string’,new Value(24))THIS\_IS\_FN(1,’string’,?);### Column

[](#column)

Arguments: (string $table\_or\_column, string $column = null, string $alias = null)

Column is part which represent column name with table (optional) and alias (optional).

We can construct it in more ways:

- new Column(‘table’, ‘column’, ‘alias’)
- new Column(‘table’, ‘column’)
- new Column(null, ‘column’, ‘alias’)
- new Column(‘table.column alias’)
- new Column(‘table.column’, null, ‘alias’)

Becouse what we give into query, is passed through Column::ensure() function, we can define column in next ways:

```
$q->select('table.column alias', ['table', 'column'], ['t', 'c', 'als'], [null, 'col', 'alias'], 'col alias');
```

### Name

[](#name)

Arguments: (string $name)

Name is like Column, but it doesn’t have table and alias. It is used for column and table names.

### JoinCondition

[](#joincondition)

Arguments: (Name $col1, $operator = null, Name $col2)

Join condition can be ON (c1 = c2) or USING (c).

```
echo new JoinCondition('col1', '=', 'col2'); // ON `col` = `col2`
echo new JoinCondition('col1', 'col2'); // same thing
echo new JoinCondition('col'); // USING (`col`)
```

Column names are automatically escaped, becouse they are transformed into Name part.

### Table

[](#table)

Arguments: (Name or Query $table, Name $alias = null)

Table is used to specify source of data. It is used in join method.

```
$q->join(new Table(Query::select()->from('tbl'), 'alias'));
$q->join(new Table('table', 'alias'));
$q->join(new Table('tbl', 'alias'));
$q->join(new Table('tbl'));

// Becouse join already make Table part, we can skip new Table:
$q->join([Query::select()->from('tbl'), 'alias']);
$q->join(['table', 'alias']);
$q->join(['tbl', 'alias']);
$q->join(['tbl']); /* same as */ $q->join('tbl');
```

Inserting data
--------------

[](#inserting-data)

1. We can insert full row: $data1 = \[1, 2, ‘value’\];
2. We can insert key=&gt;value row with default data: $data2 = \[‘text’ =&gt; ‘value’\];

```
Query::insert('table', $data1);
Query::insert('table', $data2);
// Dont forget to execute query...

// We can insert even more data at once
Query::insert('table', [$data1, $data1, $data1]); // id conflict with maybe
Query::insert('table', [$data2, $data2, $data]);

// But all data in query must have same format (full-row or key-value)
```

Updating data
-------------

[](#updating-data)

```
$q = Query::update('table', [
    'col' => 'new value',
    'col2' => 'new value',
    'col3' => Fn::CONCAT('col4', 'col5')
]);

// if we forget something
$q->set('ups', null);

// Add filter
$q->whereId(3);
```

For filters documentation check [Where and Having]() chapter.

Working with schema
===================

[](#working-with-schema)

For manipulating database schema, we have next queries:

 ::drop()[Drop](System/Database/Query/Drop.php) ::create()[Create](System/Database/Query/Create.php) ::alter()[Alter](System/Database/Query/Alter.php)Droping table
-------------

[](#droping-table)

```
Query::drop('table');
Query::drop('table')->ifExists();
```

Creating table
--------------

[](#creating-table)

```
$q = Query::create('table_name', function($q) {
    $q->integer('id')->primary();
    // ... other column definitions
});

$q->ifNotExists();
// other table properties
```

### Column types

[](#column-types)

Query support next column types:

```
$q->boolean('true_false');
$q->enum('sex', 'male', 'female', 'alien');
$q->set('digits', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
$q->smallInt('small');
$q->mediumInt('medium');
$q->integer('int');
$q->bigInt('big');
$q->decimal('decimal', $precision = 10, $scale = 5);
$q->vachar('varchar', 255);
$q->varchar('text');
$q->timestamp('timestamp');
$q->time('time');
$q->date('date');
$q->datetime('date_time');
$q->year('year');
```

Some of them are not supported on all databases. For enum and set are used varchar on sqlite database. (Check [Database/Parts/Sqlite/ColumnDef.php](Database/Parts/Sqlite/ColumnDef.php) implementation.)

### Column modifiers

[](#column-modifiers)

Columns can have next modifiers:

```
$col = $q->integer('int_col');
$col->unsigned(); // unsigned value
$col->nullable(); // default is not null
$col->default(24); // default is null
$col->primary(); // primary key
$col->autoincrement();
$col->unique(); // unique index
```

### Column references

[](#column-references)

We can make reference to other column:

```
Query::create('table', function($q) {
    $q->integer('id')->primary()->autoincrement();
});

Query::create('table2', function($q) {
    $q->integer('id')->primary()->autoincrement();
    $q->integer('id_table')->references('table.id');
});
```

### Table modifiers

[](#table-modifiers)

Table can have modifiers too:

```
$table = Query::create('table', function($q) { /* ... */ });
$table->temporary(); // create table in memory
$table->ifNotExists(); // skip if already exists

$table->option('key', 'value'); // custom option KEY VALUE

// Some of options are predefined
$table->engine('MyIsam'); // ENGINE=MyIsam
$table->charset('utf-8'); // CHARSET SET 'utf-8'
$table->defaultCharset('utf-8'); // DEFAULT CHARSET SET 'utf-8'
```

Alter table
-----------

[](#alter-table)

Alter is buggy and untested, please report bugs.

```
Query::alter('table')->addColumn('new_column', 'varchar', 255);
Query::alter('table')->addColumn('new_int_col', 'integer')->nullable();
```

```
Query::alter('table')->modifyColumn('existing_column', 'text')->nullable(false);
Query::alter('table')->modifyColumn('existing_int_column', 'bigInt')->default(123);
```

```
Query::changeColumn('existing_column', 'new_name', 'varchar', 255)->nullable();
```

Executing queries
=================

[](#executing-queries)

If we want execute query without needed any data in result, we simply call:

```
$q->execute();
```

Otherwise we use method -&gt;fetch() to get next result. If we want all results in one call we use method -&gt;fetchAll() which works exactly same way exept it returns all result in one array.

Fetch has first argument for specify which columns we want in result. If single column is passed single value is returned (scalar) if array is passed, array of values is returned.

```
$q->select('type', 'count')->from('table');

// Get first column
while ($type = $q->single()) {
    echo "Type: $type\n";
}

// Get one (named) column
while ($res = $q->get('count')) {
    echo "One column (count): $count\n";
}

// Get more columns
while ($res = $q->get(['type', 'count'])) {
    echo "{$res['type']} = {$res['count']}\n";
}

// Get row as object
while ($res = $q->get()) {
    echo "{$res->type} = {$res->count}\n";
}
```

```
$q->select('type', 'count')->from('table');

// Get first column
foreach ($q->singleAll() as $type) {
    echo "Type: $type\n";
}

// Get one (named) column
foreach ($q->all('count') as $res) {
    echo "One column (count): $count\n";
}

// Get more columns
foreach ($q->all(['type', 'count']) as $res) {
    echo "{$res['type']} = {$res['count']}\n";
}

// Get row as object
foreach ($q->all() as $res) {
    echo "{$res->type} = {$res->count}\n";
}

// Custom result transformation
$results = $q->all(['type', 'count'], function ($row) {
    return $row['type'] . ' => ' . $row['count'];
});
echo implode("\n", $results);
```

Working with objects
====================

[](#working-with-objects)

PHP PDO library has option to pack received data into an object. So if we use this feature we can fetch data like this:

```
$class = $q->fetch(MyClass::class);
assert($class instanceof MyClass);
```

But, we want using object to represent one row in database. For this purpose each class must have defined table name and primary key.

Using objects
-------------

[](#using-objects)

Table name is class name in snake\_case and primary key is ‘id’. We can change that with implement methods which returns table name and primary key.

```
class MyTable {
    public static function tableName() {
        return 'my_table_v2';
    }

    public static function primaryKey() {
        return 'id_my_table';
    }
}
```

Even if class doesn’t have these two methods, we can accessing to its table name and primary. But it must extends from QueryObject class.

```
class QueryObject {
    public static function primaryKey() {
        return 'id';
    }

    public static function tableName() {
        return snake_case(static::class);
    }
}
```

When we done this, we doesn’t need to remember table names any more:

```
Query::select()
    ->from(User::class)
    ->whereId(3)
    ->fetch(User::class);
```

NOTE: id is still column `id`. If we want find user by primary key, we should write: -&gt;where(user::primaryKey(), 3)

In background Name and Column part check if table is a class and replace it with Class::tableName().

```
$q = Query::select('Obj.id objid', 'name')
   ->from(Obj::class)
   ->join(Obj2::class, ['Obj2.id_obj', 'Obj.id']) // id is not necessary primary
   ->where('Obj2.something', true)
   ->orderBy('Obj2.sort')
   ->fetch(['objid', 'name']);
```

There is only one problem. Obj::class could have namespaces A\\B\\Obj. In this case, Query will not found Obj class, because it doesn’t exists in Query package. So, when new object is declared (in from or join method), Query will create alias in one of his sub namespaces. When column is referencing a class, Query will look in this namespace if it exists.

Name conflicts
--------------

[](#name-conflicts)

Maybe you think, what if I use two classes with same name in different namespaces?

```
namespace General {
    class User { } // tablename = users
}

namespace Deleted {
    class User { } // tablename = deleted_users
}

Query::select()
    ->from(General\User::class)
    ->from(Deleted\User::class)
    ->where('User.id', 3); // which one?

```

If you really want to have same object names, you can create class\_alias(Existing::class, ‘NewName’) and use alias.

There is no problem using objects with same names on different databases. Query handle that properly.

```
$q1 = Query::select()
    ->from(mysql\User::class)
    ->where('User.id', 1); // referencing Query\Aliases\Mysql\User -> mysql\User

$q2 = Query::select()
    ->from(sqlite\User::class)
    ->where('User.id', 1); // referencing Query\Aliases\Sqlite\User -> sqlite\User

Query::withConnection('mysql', function() {
    $q1->fetch();
});

Query::withConnection('sqlite', function() {
    $q2->fetch();
});
```

Different connections will have different namespaces (eg: Query\\Aliases\\&lt;Connection&gt;\\&lt;Alias&gt;) for used object aliases. But one connection must have unique names for objects.

This was a long chapter.

Joining with objects
--------------------

[](#joining-with-objects)

Joins from previous chapter works, but they are not good. We still wneed to know relation between objects, their table names and their id columns.

```
Query::select()
    ->from(User::class)
    ->join(Group::class, ['group.id', 'user.group_id']);
```

If we want write this right, we should do it in that way:

```
Query::select()
    ->from(User::class)
    ->join(Group::class, [Group::class . '.' . Group::primaryKey(),
                          User::class . '.group_id']);
```

Uff… and we still need to know `group\_id’ column everywhere in code.

### Relation definitions

[](#relation-definitions)

Basic idea is to write join definition into model and then use it all over the code.

```
class User extends QueryObject {
    protected class refGroup() {
        return Reference::toOne(Group::class, 'id_group');
    }
}

class Group extends QueryObject {
    protected class refUsers() {
        return Reference::toMany(User::class, 'id_group');
    }
}
```

Now we can start joining:

```
Query::select('g.name')
    ->from(User::class)
    ->join('User.group g');
// and
Query::count()
    ->from(Group::class, 'g')
    ->join('Group.users')
    ->where('g.name', 'Guest')
    ->single();
```

Problems:

- \[ \] Table can be aliased
- \[ \] …

#### Devel

[](#devel)

```
class Model {
    public function tableName() {
        return 'ime_tabele';
    }
    public function primary() {
        return 'ID_ime_tabele';
    }
}
```

```
class Query {
    private $components = [];   // Unused...
    private $bindings = [];
    private $sources = [
        'table or alias' => 'source info',
        'alias' => 'table_name',
        'table_name2' => 'table_name2',
        // 'g' => new Source('groups', 'g')
    ];
    private $select = [
        'name' => 'whatever(*)',
        'count' => new Column(),
        'xxx' => new Raw()
    ];
}
```

```
abstract class Source {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function name() {
        return $this->name;
    }

    abstract public function primary();
    abstract public function table();

    public function debug() {
        echo "Table: `" . $this->name() . "` primary: `" . $this->primary() . "`\n";
    }
}
```

```
class TableSource extends Source {
    private $table;

    public function __construct($table, $name = null) {
        parent::__construct($name ?: $table);
        $this->table = $table;
    }

    public function primary() {
        return 'id';
    }

    public function table() {
        return $table;
    }
}
```

```
class ModelSource extends TableSource {
    private $model;

    public function __construct($model, $name = null) {
        $this->setModel($model);
        parent::__construct($model::tableName(), $name);
    }

    private function setModel($model) {
        // if (class_exists($model) and is_subclass_of($model, QueryObject::class)) {
        if (class_exists($model)) {
            $this->model = $model;
        } else {
            throw new Exception("Wrong class name '$model'"); // Should be QueryException
        }
    }

    public function primary() {
        $m =  $this->model;
        return $m::primary();
    }

    public function table() {
        $m = $this->model;
        return $m::tableName();
    }
};
```

```
$s1 = new ModelSource(Model::class);
$s2 = new ModelSource(Model::class, 'alias');
$s3 = new TableSource('tabela', 'alias');
echo "\n";
$s1->debug();
$s2->debug();
$s3->debug();
```

```
Table: `ime_tabele` primary: `ID_ime_tabele`
Table: `alias` primary: `ID_ime_tabele`
Table: `alias` primary: `id`
```

##### Query

[](#query)

```
class Query {
    // private $components = [];   // Unused...
    private $bindings = [];
    private $sources = [
        'table or alias' => 'source info',
        'alias' => 'table_name',
        'table_name2' => 'table_name2',
        // 'g' => new Source('groups', 'g')
    ];

    // private $select = [
    //     'name' => 'whatever(*)',
    //     'count' => new Column(),
    //     'xxx' => new Raw()
    // ];

    public function from($source, $alias = null) {
        if (class_exists($source)) {
            // it is model
            $source = new ModelSource($source, $alias);
        } else {
            $source = new TableSource($source, $alias);
        }

        $sources[$source->name()] = $source;
    }
}
```

Relations
---------

[](#relations)

- Note taken on \[2017-05-01 Mon 21:12\]
     Popravi query, da bo selektal samo prvo from tabelo (v primeru modelov)
- Note taken on \[2017-05-01 Mon 21:11\]
     ‘id’ v querijih ni nujno primarni kljuc

```
class Person as QueryObject {
    public static function relGroup() {
        return self::reference()
            ->hasOne(Group::class, 'group_id');
    }

    public static function relPosts() {
        return self::reference()
            ->hasMany(Post::class, 'person_id');
    }
}
```

```
Query::select()
    ->from(Person::class)
    ->join('Person.group g')
    ->join('Person.posts posts')
    ->groupBy(Person::class)
    ->where('g.name', 'Regular user')
    ->having(Fn::count('posts.!id!'), '>', 10)
    ->all();
```

QueryObject
===========

[](#queryobject)

If we want using objects in Query, they must extends QueryObject. If we want different tablename or primary key for object, we can override tableName() or primaryKey() function.

In case we want totaly different rules to generate tablenames and primary keys, we can use our own QueryObject class which extends original QueryObject.

```
class QueryObject {
    public static function tableName() {
        return camel_case(drop_namespace(QueryObject::class));
    }

    public static function primaryKey() {
        return 'id';
    }
}
```

###  Health Score

24

—

LowBetter than 31% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

2471d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2e72674f3996339810fecca431793ab66131a38880760fa342ac5e2b61432ed4?d=identicon)[SilverEngineTeam](/maintainers/SilverEngineTeam)

---

Top Contributors

[![Nejcc](https://avatars.githubusercontent.com/u/6236128?v=4)](https://github.com/Nejcc "Nejcc (5 commits)")

### Embed Badge

![Health badge](/badges/silverengine-reflectorm/health.svg)

```
[![Health](https://phpackages.com/badges/silverengine-reflectorm/health.svg)](https://phpackages.com/packages/silverengine-reflectorm)
```

###  Alternatives

[ckeditor/ckeditor

JavaScript WYSIWYG web text editor.

5454.3M79](/packages/ckeditor-ckeditor)[permafrost-dev/phpcsfixer-preset

shared php-cs-fixer rules &amp; finders preset

226.6k1](/packages/permafrost-dev-phpcsfixer-preset)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
