Developer Blog

  • Blog
  • /
  • New PHPixie Database and Configuration Libraries
By Dracony on 16 March 2014

Since some of you have been anxious to see what PHPixie v3 is going to be and in order to get some feedback I decided to release two finished libraries from the third branch. And yes, I’m callng them ‘libraries’ and not ‘modules’ because they are now decoupled from the framework and can be used with any project.

They are PSR-2 compliant and fully tested with 100% code coverage.
https://rawgithub.com/PHPixie/Config/master/tests/report/index.html
https://rawgithub.com/PHPixie/Database/master/tests/report/index.html

I haven’t gotten around to writing documentation for classes yet though, obviously I’ll add them before releasing the next version of the framework. This post is going to be a quick introduction into the available features.

Configuration Library
Has now been moved to a separate library and adds new ways of defining and accessing your settings. Three configuration storages are now supported:

  • File – The one you got used to in PHPixie v2
  • Data – Similar to File, but you initialize it with an array instead of pointing to a file
  • Directory – The most awesome one, more on it a bit later

As usual you can access your values using the dot notation $config->get(‘forest.meadow.fairy.name’), but what if you have a Fairy class that should know only about what is under the forest.meadow.fairy and not know about hwo other configuration options are layed out? Also what if you swap ‘meadow’ for ‘field’, now you have to replace all references to that options in all classes, but now we have a solution.

Introducing Slices
You can slice your configuration to get a subset of it:

1
2
3
4
5
6
//instead of
$config->get('forest.meadow.fairy.name')

//you can do
$meadowConfig = $config->slice('forest.meadow');
$meadowConfig->get('fairy.name');

So now as you go deeper into class hierarchy you can slice your config as you go and pass it along.
Slices have additional methods like set() and remove() which allow you to modify configuration options and changes that you make to one slice will populate to other slices. Calling persist() on the storage after any of the slices has been modified will save your new options.

Directory Storage
Allows you to use both files and folders to specify configuration options, consider the following directory structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
config.php
config/
--forest.php
--forest/
----meadow/
------fairy.php

And here is the order in which it will look for your option,
if some file doesn't exsist it will skip that possibility.

1) 'name' in config/forest/meadow/fairy.php
2) 'fairy.name' in config/forest/meadow.php
3) 'meadow.forest.name' in config/forest.php
4) 'forest.meadow.forest.name' in forest.php

The fact that your configuration is split into files is entirely hidden, so that if you do $config->get(‘forest’) it will retrieve all of the data as one single array with nested subarrays.

Combining directory storage with slicing will make a perfect solution for allowing multiple languages on your site, for example you could pass $config->slice(‘lang.english’) to your view and the use $slice->get(‘cat’, ‘cat’) to get translations, with default value specified for when it’s not translated yet.

Database Module
The database module has been hugely updated and there is a lot to talk about, so lets instead focus on a few examples that utilize it to give you a good impression of whats inside.

To install use this in your composer.json

1
2
3
4
5
6
{
    "require": {
        "phpixie/config": "3.*@dev",
        "phpixie/database": "3.*@dev"
        }
}

And lets give it a go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
require_once( __DIR__.'/vendor/autoload.php');

//GEt configuration library
$pixieConfig = new \PHPixie\Config;

//Create config from array
$config = $pixieConfig->dataStorage(array(

    //MongoDB connection
    'default' => array(
        'connection' => 'mongodb://localhost',
        'driver' => 'mongo',
        'user' => 'mongouser',
        'password' => 'mongopass',
        'database' => 'test'
    ),

    //Mysql connection
    'mysql' => array(
        'connection' => 'mysql:host=localhost;dbname=pixies',
        'driver' => 'pdo',
        'user' => 'root',
        'password' => ''
    )
));

//Now initialize database library with our config
$pixieDatabase = new \PHPixie\Database($config);

//Get the 'default' connection, MongoDB in our case
$conn = $pixieDatabase->get();

//Inserting entities
$conn->query('insert')
                ->collection('fairies')
                ->data(array(
                    'name' => 'Trixie',
                    'color' => 'blue',
                    'pets' => array(
                        'foxes' => 3
                    ),
                    'about' => array(
                        'likes' => array('singing', 'flying')
                    )
                ))
                ->execute();

//Inserting multiple entities at once
$conn->query('insert')
                ->collection('fairies')
                ->batchData(array(
                    array(
                        'name' => 'Tinkerbell',
                        'color' => 'green',
                        'about' => array(
                            'likes' => array('singing', 'dancing')
                        )
                    ),
                    array(
                        'name' => 'Flora',
                        'color' => 'yellow',
                        'about' => array(
                            'likes' => array('sleeping')
                        )
                    )
                ))
                ->execute();

//Using the query builder we get an equivalent of
//name = 'Trixie' OR (about.likes has 'dancing' AND about.likes has 'singing')
$fairies = $conn->query('select')
                        ->collection('fairies')
                        ->where('name', 'Trixie')
                        ->orWhere(function($q) {
                            $q
                                ->_and('about.likes', 'dancing')
                                ->_and('about.likes', 'singing');
                        })
                        ->orderBy('name', 'asc')
                        ->limit(2)
                        ->execute()
                        ->asArray();
print_r($fairies);

//Updating some fields
$conn->query('update')
                    ->collection('fairies')
                    ->data(array(
                        'pets' => array(
                            'foxes' => 5
                        )
                    ))
                    ->whereNot('pets.foxes', '>', 5)
                    ->execute();

//Deleting
$conn->query('delete')
                    ->collection('fairies')
                    ->where('name', 'Tinkerbell')
                    ->execute();

//Take a look at what got left in the database
$fairies = $conn->query('select')
                        ->collection('fairies')
                        ->execute()
                        ->asArray();
print_r($fairies);

//And clean up by deleting everything
$conn->query('delete')
                    ->collection('fairies')
                    ->execute();


//Now lets try MySQL
$conn = $pixieDatabase->get('mysql');

//Note how batch insertion has different syntax for SQL queries
//The first parameter defines columns while the second one defines rows
$fairies = $conn->query('insert')
                        ->table('fairies')
                        ->batchData(array('name', 'likes', 'pets', 'flowers'), array(
                            array('Tinkerbell', 'singing', 4, 5),
                            array('Tinkerbell', 'dancing', 6, 2)
                        ));

//Selecting fields with aliases and joinging tables
//You can define more complex on() conditions
//using methods like orOn(), etc..
$fairies = $conn->query('select')
                        ->fields(array(
                            'fairy' => 'fairies.name',
                            'trees' => 'trees.name'
                        ))
                        ->table('fairies')
                        ->join('trees')
                        ->on('fairies.id', 'trees.fairy_id')
                        ->execute()
                        ->asArray();

print_r($fairies);

//The '*' after an operator
//specifies that 'flowers' is a name of a column
//and not a string value
$fairies = $conn->query('select')
                        ->table('fairies')
                        ->where('pets', '>*', 'flowers')
                        ->execute()
                        ->asArray();

print_r($fairies);

There is going to be much more documentation on both modules coming when the rest of the framework is done, but I hope this was be enough to get you to try using it. As I said, it is already tested an working properly, so you might as well give it a go now =)

P.S. The last week I’ve been working day and literally night to get this done, tested and released so finally I’m going to get some rest =)

comments powered by Disqus