I have a long php array which I loop through and create a form with. I am going to create a YAML file for it, for ease of maintenance. My problem is I don't know where the appropriate place is to store this file, and how would i invoke it from the view.
You have stated that your wish is to store this data in YAML format. In MVC terminology, this is now your data source.
You can store your YAML file(s) anywhere on the system, but assuming you wish to store these under version control and don't wish them to be publically accessible, a suitable place would be anywhere in your app/
directory outside of app/webroot/
.
As far as I know, there aren't any existing datasources designed to read (and/or write) from YAML files currently, so this would be an undertaking for yourself if you wish to adhere strictly the the MVC structure.
There are several example datasources available on github and other places for you to reference. It is worth noting that there are two common approaches to creating datasources.
- The simple approach (and most common) is mainly concerned with the reading/writing of the data and tend to use custom methods in their datasource which the model can call. This is the fastest approach, allowing you to pull in data which doesn't necesarilly have to groove with all CakePHP's conventions.
- The more advanced approach (and possibly preferred by cake core devs) is to properly reimplement most of the methods in the class you are extending (either DataSource or, in the case of databases, DboSource). This allows CakePHP to do it's magic when performing common tasks such as
find('list')
orpaginate()
.
I would personally advise starting with the simple approach like so:
app/config/database.php
:
var $yaml = array(
'datasource' => 'yaml',
'path' => 'data/yaml', // relative to app/ directory, no trailing slash
);
app/models/category.php
:
// app/models/category.php
class Category extends AppModel {
public $useDbConfig = 'yaml';
/**
* cake should automatically set $model->table to 'categories' on construct
* (http://api.cakephp.org/view_source/model/#l-419) hence $useTable will not
* be necessary (convention over configuration)
*/
// public $useTable = 'categories';
/**
* since datasource doesn't strictly adhere to cake's api, the core find
* method may cause errors. to combat this we can redefine this method with
* a much simpler version, but functionality will be lost in doing so.
*/
public function find($type = 'all', $options = array()) {
// get an instance of our datasource
$ds = ConnectionManager::getDataSource($this->useDbConfig);
// $type is usually handled by model, but we will pass it to datasource
$options['type'] = $type;
// query the datasource
return $ds->read($this, $options);
}
}
app/models/datasources/yaml_source.php
:
class YamlSource extends DataSource {
public function read(&$model, $queryData = array()) {
// determine path to yaml file
$yamlPath = APP . $this->config['path'] . DS . $model->table . '.yaml';
// parse the yaml file
$results = $this->_parseYaml($yamlPath);
// perform any array manipulation (determined by $queryData)
...
// handle $queryData['type'] (would normally have been done in Model)
...
// return results in a typical Model data array
return array($model->alias => array(
$results // your results go here
));
}
}
app/controllers/categories_controller.php
:
class PostsController extends AppController {
/**
* this is only needed if all actions use these models. Since Post model is
* loaded automatically, we can just use Controller::loadModel() to get an
* instance of Category model where needed.
*/
//public $uses = array('Post', 'Category');
public function edit($id) {
$posts = $this->Post->read(null, $id);
if (!$post) {
$this->cakeError('error404');
}
$this->loadModel('Category');
$categories = $this->Category->find('list');
$this->set(compact('posts', 'categories'));
}
}
You will be able to expand on this datasource through time (making it more compatible with the API) as you need more features (pagination, callback filters, relationships, etc). This datasource will be immediately reusable by any of your other application(s') models.
A simpler approach (read: more basic) which still answers your question, but ignores the fact you want to store the static data in a yaml file, would be as follows:
// app/models/category.php
class Category extends AppModel {
public $name = 'yaml';
public $data = array(
array(
'id' => '1',
'name' => 'Category 1',
),
array(
'id' => '2',
'name' => 'Category 2',
),
...
);
public function find($type = 'all', $options = array()) {
if ($type == 'all') {
return $this->data;
}
// .. otherwise do some array manipulation on $this->data ..
return $results;
}
}
This approach doesn't have the same extensibility or reusability as using a datasource.
I would not do it either of these ways. I would not use YAML either. I would create your "long PHP array" as a, wait for it, long PHP array.
There is not much difference to storing the data as a PHP array to storing it as YAML. Furthermore, if you store it as an array, you don't then need to convert it from YAML to an array in order to loop through it.
The appropriate place for this array is a file that you load in with the Configure class. E.g. /app/config/form.php:
<?php
$config = array(
'Form' => array(
...
)
);
?>
You can then load the array from wherever you like in your app and access it
<?php
Configure::load('form'); // Loads app/config/form.php file and stores $config in Configure class
$form = Configure::read('Form');
foreach ($form as $field) {
...
}
?>