Skip to end of metadata
Go to start of metadata

<p>So, now you have seen how to add commands to the zf tooling system. But what if you want to interact with a "Project". I guess the bigger question is, what is a "Project"? In general, a "project" is a planned endeavor or an initiative. In the computer world, projects generally are a collection of resources. These resources can be files, directories, databases, schemas, images, styles, and more. </p>

<p>This same concept applies to Zend Framework projects. In ZF projects, you have controllers, actions, views, databases and so on and so forth. In terms of Zend_Tool, we need a way to track these types of resources - thus Zend_Tool_Project.</p>

<p>Zend_Tool_Project is capable of tracking project resources throughout the development of a project. So, for example, if in one command you created a controller, and in the next command you wish to create an action within that controller, Zend_Tool_Project is gonna have to <strong>know</strong> about the controller file you created so that you can (in the next action), be able to append that action to it. This is what keeps our projects up to date and <strong>stateful</strong>.</p>

<p>Another important point to understand about projects is that typically, resources are organized in a hierarchical fashion. With that in mind, Zend_Tool_Project is capable of serializing the current project into a internal representation that allows it to keep track of not only <strong>what</strong> resources are part of a project at any given time, but also <strong>where</strong> they are in relation to one another.</p>

<p>So, with that out of the way, lets get to the code. In the below example, we will create a provider that will expose a command called "create mymodel" which will be a very simple model in our existing project.</p>

<p>As you might recall from Part 1, we first need to create a provider which we will name "My_ModelProvider". At this point, since our new provider will be Zend_Tool_Project specific, we will extend Zend_Tool_Project's special Provider Abstract. By extending this special provider abstract, we not only are implementing the Zend_Tool_Framework_Provider_Interface, but we are also gaining some common functionality for "Projects" that we will need.</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
class My_ModelProvider implements Zend_Tool_Project_Provider_Abstract
{

public function create($modelname = "Model")
{

}

}
]]></ac:plain-text-body></ac:macro>

<p>As you can see, this is all we need to have in order to expose a "create" method for our "model" provider. (Remember, this file will have to exist inside your include_path, at the location My/ModelProvider.php to be found by the Zend_Tool system.)</p>

<p>At this point we have a provider that can be dispatched and essentially won't do much of anything useful as you can see by the empty method body for create. Our next step is to provide that method body which will include some of our own custom logic as well as some logical that is specific to how <strong>new resources are integrated with existing projects</strong>.</p>

<p>Now is a good time to understand how projects are stored with respect to Zend_Tool_Project. To gain this insight, lets go back to our article on <a href="">getting started with Zend_Tool</a>. After the step in that article where we have run "zf create project", lets have a look at the hidden file that was created. It can be found inside the project directory with a name of ".zfproject.xml". It should look like this:</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
<?xml version="1.0"?>
<projectProfile>
<projectDirectory>
<projectProfileFile/>
<applicationDirectory>
<apisDirectory enabled="false"/>
<configsDirectory/>
<controllersDirectory>
<controllerFile controllerName="index"/>
<controllerFile controllerName="error"/>
</controllersDirectory>
<layoutsDirectory enabled="false"/>
<modelsDirectory/>
<modulesDirectory enabled="false"/>
<viewsDirectory>
<viewScriptsDirectory>
<viewControllerScriptsDirectory forControllerName="index">
<viewScriptFile scriptName="index"/>
</viewControllerScriptsDirectory>
<viewControllerScriptsDirectory forControllerName="error">
<viewScriptFile scriptName="error"/>
</viewControllerScriptsDirectory>
</viewScriptsDirectory>
<viewHelpersDirectory/>
<viewFiltersDirectory enabled="false"/>
</viewsDirectory>
<bootstrapFile/>
</applicationDirectory>
<dataDirectory enabled="false">
<cacheDirectory enabled="false"/>
<searchIndexesDirectory enabled="false"/>
<localesDirectory enabled="false"/>
<logsDirectory enabled="false"/>
<sessionsDirectory enabled="false"/>
<uploadsDirectory enabled="false"/>
</dataDirectory>
<libraryDirectory>
<zfStandardLibraryDirectory/>
</libraryDirectory>
<publicDirectory>
<publicStylesheetsDirectory enabled="false"/>
<publicScriptsDirectory enabled="false"/>
<publicImagesDirectory enabled="false"/>
<publicIndexFile/>
<htaccessFile/>
</publicDirectory>
<providersDirectory enabled="false"/>
</projectDirectory>
</projectProfile>
]]></ac:plain-text-body></ac:macro>

<p>This file represents how Zend_Tool_Project manages resources in a hierarchical fashion. For the purposes of our example, we want to be able to add our new "Models" into the "ModelsDirectory" of this project. In addition, we want to make sure that our project understands the context of these new resources. What is meant by "context" is so that our project understands what "role" this new resources plays within our project. For example, first and foremost, our "Model" resource is a file, so the project knows that this can be written to disk when asked to. Also, it understand that its a "Model" resource, and can have some custom logic assigned to it so that is <strong>acts</strong> like a Model with respect to other resources.</p>

<p>To be able to assign a context to these resources when they are created, we will also need to create a context class that we can use.</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
<?php

require_once 'Zend/Tool/Project/Context/Filesystem/Directory.php';

class Example_MyModelFileContext extends Zend_Tool_Project_Context_Filesystem_File
{
// proteted property to hold the actual models name
protected $_modelName = null;

// this is the name of the context (you will see this written as the node name of the project file)
public function getName()

Unknown macro: { return 'ModelFile'; }

// anything returned here will be "Attributes" of the context and persisted throughout all project interactions
public function getPersistentParameters()

Unknown macro: { return array('modelName' => $this->_modelName); }

// this is an accessor for setting the modelName
public function setModelName($modelName)

Unknown macro: { $this->_modelName = $modelName; $this->_filesystemName = $modelName . '.php'; }

// this is an accessor for getting the modelname
public function getModelName()

Unknown macro: { return $this->_modelName; }

}
]]></ac:plain-text-body></ac:macro>

<p>Now that we have a context, we can start interacting with our project when our create method is called. Lets walk through the code sample below line by line (there will be comments before each line).</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
class My_ModelProvider implements Zend_Tool_Project_Provider_Abstract
{

public function create($modelname = "Model")
{

// find where we need to write these new model files
// a graph is the serialized structure as objects
$projectGraph = $this->_loadProjectProfile();

// Now, lets create a new node within our project that will be defined
// by the context we created previously under the existing 'modelsDirectory'
try

Unknown macro: { $modelFileNode = $this->_createNodeUnder('modelsDirectory'); }

catch (Exception $e)

Unknown macro: { throw new Zend_Tool_Framework_Exception('This project doesnt have a models directory'); }

// lets give our new model its name provided by the caller.
$modelFileNode->setModelName($modelname);

echo 'Creating model file for ' . $modelName . PHP_EOL;
$modelFileNode->create();

// call out to our local method for putting "code" into our new model file within
// our project
$this->_createMyModelContents($modelFileNode->getPath(), $modelname);

$this->_storeProfile();
}

protected function _createMyModelContents($path, $modelName)
{
$contents = <<<EOS
class {$modelName}
{

protected \$_data = null;

public function setData(\$data)

Unknown macro: { $this->_data = $data; return $this; }

public function getData()

Unknown macro: { return $this->_data; }

}

EOS;

file_put_contents($path, $contents);

}

}
]]></ac:plain-text-body></ac:macro>

<p>Now that we have those two file in place, its just a matter or running the command:</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
zf create model --modelname Foo
]]></ac:plain-text-body></ac:macro>

<p>And there you go, now you have created both a provider and a project context so that you can add custom resources to an existing project!</p>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Dec 03, 2008

    <p>You might consider using a 'zf' namespace in the XML. This would make it possible to do some validation on loading the file and would separate the nodes provided by the standard library from others.<br />
    I'm still not seeing the clear distinction between a node and a context in this doc. Is the context simply the name of a node? Why can't we simply have a method called $graph->findNodeByName() instead of introducing a new concept of 'context'? How do you plan to handle things like controller providers in cases where you have multiple modules? In these cases, I don't think it would suffice to just look up the node using the 'controllerDirectory' name. Do contexts handle this? Is this why they have to be more complex than a name in the form of a string?<br />
    Nano-comment: consider changing enabled='true' to something shorter and more specific like exists='no' to make the XML doc more readable.</p>