MVC Developers Part 4 – models and Basics ORM

  • mvc abstract model database php

The implementation of a “Tier Models” is a large part of an MVC framework. It represents the details of your application, and most applications are useless without data. Magento Models play an even greater role, because they usually contain english dissertation the “Business Logic” that often (good or bad) are relegated to the controller helper methods or other PHP MVC frameworks.

The following is part of a longer series on Magento aimed at developers familiar with PHP MVC development. Although each article can be read stand alone, each article is based on concepts discussed in earlier articles. If you are confused, especially to catch up on the earlier work first.

Traditional PHP MVC Models

If the definition of MVC is somewhat vague, the definition of design is even vaguer. Prior to the widespread adoption of the MVC pattern by PHP developers, data access is Usually raw SQL statements and / or an SQL abstraction layer (like ADODB). Developers would write questions and not think too much about those articles were modeling.

In 2009 raw SQL is usually rejected, but many PHP frameworks are SQL centric. Models are objects that some abstraction layer to offer, but behind the scenes are still developers write SQL and / or call as SQL abstraction methods to read and write-down of their data.

Other frameworks SQL warning and take the Object Relational Mapping (ORM) approach. Here is a developer strictly dealing with objects. Properties are set, and store when a method is called on the object, the data is automatically written to the database. Some ORMS will attempt to defend divine attributes from the database, others require the user to give them one way or another (usually in an abstract data such as language YAML). One of the most famous and popular implementations of this approach is ActiveRecord.

This definition of ORM should suffice for now, but like everything these days of Computer Science, the strict definition of ORM has faded over the years. It’s beyond the scope of this article, to settle disputes, but suffice it to say that I am generalizing a bit, and probably ask to be jumped by a couple of Java developers when I leave the house.

Magento Models

It should come as no surprise that the Magento ORM approach will be. While the Zend Framework SQL abstractions are available, most of your data access will be through the built Magento models and build your own. It should be no surprise that Magento is a very flexible, very abstract and sometimes confusing concept of what a model has.

Anatomy of a Magento Model

Magento Most models can be classified into one of the two way. There is a basic, ActiveRecord-like/one-object-one-table model, nor is there a Entity Attribute Value (EAV) model. There is also Model Collections. Collections are PHP objects used to model a number of individual cases to Magento. The team has performed Varien the PHP standard library interfaces and Countable IteratorAggregate for each model type to have its own collection type. If you are not familiar with the Standard PHP Library Collections think of design as arrays also have attached methods.

Magento models have a code in to connect to the database. Instead, each model has two modelResouces (one read, one write) used to communicate with the database server (read and write using adapter objects). By decoupling the logical model and the code that talks to the database, it is theoretically possible to create new resource classes to register for another database platform, while keeping your models itself untouched.

Creating a Basic Model

Firstly we go to a basic model to create Magento. PHP MVC tradition insists we model a blog post. The steps we have to be

1. Crete a new “blog” module
2. Create a database table for our Model
3. Add design information to the config for a model called blog post
4. Resource model add information to the config for the blog post Model
5. Add an adapter to read the config for the blog post Model
6. Write an adapter to add the config for the blog post Model
7. Add a PHP class file for the blog post Model
8. Add a PHP class file for the blog post Model
9. A copy of the Model

Create a Weblog Module

You need an old hat on the creation of empty modules at this point, so I skip the details and assume you make an empty module called blog. After you’ve done that, we will be setting up a route for an index with an Action Controller action called “test model”. As always, the following examples are based on a package name “Alan Storm Dotcom.

In Alanstormdotcom/Weblog/etc/config.xml, set the following route

<frontend>
    <routers>
        <weblog>
            <use>standard</use>
            <args>
                <module>Alanstormdotcom_Weblog</module>
                <frontName>weblog</frontName>
            </args>
        </weblog>
    </routers>
</frontend>    

And add the following Action Controller

class Alanstormdotcom_Weblog_IndexController extends Mage_Core_Controller_Front_Action {
    public function testModelAction() {
        echo 'Setup!';
    }
}

Alanstormdotcom/Weblog/controllers/IndexController.php. Clear your cache and Magento loading the following URL to setup everything was correct protection.

http://example.com/weblog/index/testModel

You need the word “Setup” on a white background.

Creating the Database Table

Magento is a system for automatically creating and modifying your database schema, but for now we just manually create a table for our model.

Using the command line or your favorite MySQL GUI application, create a table with the following schedule

CREATE TABLE `blog_posts` (
  `blogpost_id` int(11) NOT NULL auto_increment,
  `title` text,
  `post` text,
  `date` datetime default NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY  (`blogpost_id`)
)

And then populate it with some data

INSERT INTO `blog_posts` VALUES (1,'My New Title','This is a blog post','2009-07-01 00:00:00','2009-07-02 23:12:30');

The Global Config and Creating The Model

There are four distinct things we need to setup for a model in our config.

1. Enabling Models in our module

2. Enabling Model sources in our module

3. Add an “entity” to our Resource Model. For simple models, this is the name of the table

4. Read specifying an adapter for our specific model Resource

5. Write specifying an adapter for our specific model Resource
When you instantiate a model in Magento, you make a call like this

$model = Mage::getModel('weblog/blogpost');

The first part of the URI passes you get the group design model. By the way Magento __autoloads classes, it also has the (lowercase) the name of your module. The second part of the URI is the small version of the name of your model.

So, let’s add the following to our module’s config.xml XML.

<global>
    <!-- ... -->
    <models>
        <weblog>
            <class>Alanstormdotcom_Weblog_Model</class>
            <!--
            need to create our own resource, can't just
            use core_mysql4
            -->
            <resourceModel>weblog_mysql4</resourceModel>
        </weblog>
    </models>
    <!-- ... -->
</global>

The outer <weblog /> tag is your group that your module name must match. <class /> BASE is the name of all models in the group blog will have. The <resourceModel /> tag indicates the Resource Model, which models to use group blog. There is more information about this below, but for now happy to know that your group name, followed by the literal string “MySQL4.

So we’re not finished yet, but let’s see what happens when we clear our Magento cache and attempt to instantiate a blog post design. In your Action Model test method, use the following code

public function testModelAction() {
        $blogpost = Mage::getModel('weblog/blogpost');
        echo get_class($blogpost);
    }

and load your page. You will see an exception that looks something like this (make sure you’ve enabled developer mode).

include(Alanstormdotcom/Weblog/Model/Blogpost.php) [function.include]: failed to open stream: No such file or directory

include (Alan Storm Dotcom / blog / model / Blogpost.php) [function.include]: failed to open stream: No such file or directory

In an attempt at aweblog/blogpost post retrieval model, you said Magento instantiate a class named

Alanstormdotcom_Weblog_Model_Blogpost

Magento is trying to __autoload this model, but can not find the file. Let’s make! Create the following classes at the following location

File: app/code/local/Alanstormdotcom/Weblog/Model/Blogpost.php

class Alanstormdotcom_Weblog_Model_Blogpost extends Mage_Core_Model_Abstract
{
    protected function _construct()
    {
        $this->_init('weblog/blogpost');
    }
}

Reload your page, and the exception should be replaced by the name of your class.

All basic models should extend Mage_Core_Model_Abstract class. This abstract class forces you to a single method named _construct (Note: this is not the maker of PHP __consutrct necessary.) This method should call the class with the same identifier URI _init method you’ll use in Mage:: getModel method.

The Global Config and Resources

So, we have our model setup. Then we setup our Resource Model. Resources model the code that actually calls to our database. In the latter part we include the following in our config.

<resourceModel>weblog_mysql4</resourceModel>

The value in <resourceModel /> will be used to instantiate a class resource model. Although you’ll never have it for yourself if a design blog in the group should talk to the database call, Magento will create the following method call to get the source model

Mage::getResourceModel('weblog/blogpost');

Again, the group’s blog, and blog post is the model. The Mage:: getResourceModel method will use the blog / blog post on the URI global configuration to inspect and remove the value <resourceModel> (in this case, weblog_mysql4). Then a model class instantiated with the following URI

weblog_mysql4/blogpost

So, if you followed all the way, what this means is, resource models are configured in the same section of the XML configuration as standard models. This can be confusing to newcomers and old hands.

So with that in mind, let’s configure our source. In our section <models> add

<global>
    <!-- ... -->
    <models>
        <!-- ... -->
        <weblog_mysql4>
            <class>Alanstormdotcom_Weblog_Model_Mysql4</class>
        </weblog_mysql4>
    </models>
</global>

You’re adding the tag <weblog_mysql4 /> that the value of the tag <resourceModel /> you just setup. The value of <class /> is the base name that all modes will have your source, and must be called with the following format

Packagename_Modulename_Model_Mysql4

So, we have configured a resource, let us try to load some data model. Change your action to look like the following

public function testModelAction() {
    $params = $this->getRequest()->getParams();
    $blogpost = Mage::getModel('weblog/blogpost');
    echo("Loading the blogpost with an ID of ".$params['id']);
    $blogpost->load($params['id']);
    $data = $blogpost->getData();
    var_dump($data);
}

Then insert the following URL into your browser (after clearing your cache Magento)

http://example.com/weblog/index/testModel/id/1

You will see an exception like the following

Warning: include(Alanstormdotcom/Weblog/Model/Mysql4/Blogpost.php) [function.include]: failed to open stream: No such file ....

As you probably intuitively, we need a resource for our class design to add. Each model has its own resource class. Add the following class at the following location

File: app/code/local/Alanstormdotcom/Weblog/Model/Mysql4/Blogpost.php

class Alanstormdotcom_Weblog_Model_Mysql4_Blogpost extends Mage_Core_Model_Mysql4_Abstract{
    protected function _construct()
    {
        $this->_init('weblog/blogpost', 'blogpost_id');
    }
}

Again, the first parameter of the init method is the URL used to identify the model. The second parameter is the database field that uniquely identifies a particular column. In most cases this should be the primary key. Clear your cache, reload, and you should see

Loading the blogpost with an ID of 1

array
empty

Therefore, we got things to the point where there is no exception, but we are still not read from the database. What gives?

Each group has an adapter Model read (for reading from the database) and write an adapter (to update the database). Magento offers a model to the standard adapter, or for developers to use their own writing. Anyway, we should talk about Magento. We add a new section called the tag <resources /> <global /> part of our module config.

<global>
    <!-- ... -->
    <resources>
        <weblog_write>
            <connection>
                <use>core_write</use>
            </connection>
        </weblog_write>
        <weblog_read>
            <connection>
                <use>core_read</use>
            </connection>
        </weblog_read>
    </resources>
</global>

We add two sub-sections <resources />. One for writing and one for reading. The tag names (and <weblog_write /> <weblog_read />) are based on the group we described above (“weblog”).

OK, certainly with our adapter in place we are ready. Let us clear our cache, reload the page, and …

Can't retrieve entity config: weblog/blogpost

Another exception! When we use the model URI weblog / blog post, tell us we want the Magneto Model Group weblog, the blog post Entity. In the context of simple models Mage_Core_Model_Mysql4_Abstract to extend an entity corresponds to a table. In this case the table name we made blog_post above. Let this entity to add to our XML config.

<models>
        <!-- ... --->
        <weblog_mysql4>
            <class>Alanstormdotcom_Weblog_Model_Mysql4</class>
            <entities>
                <blogpost>
                    <table>blog_posts</table>
                </blogpost>
            </entities>
        </weblog_mysql4>
    </models>

We have a new section to the source <entities /> Model portion of our config. This in turn has a section named after our entity (<blogpost />) that specifies the name of the database table we want to use for this model.

Create your Magento cache, cross your fingers, and reload the page …

Loading the blogpost with an ID of 2

Loading the blogpost with an ID of 1

array
  'blogpost_id' => string '1' (length=1)
  'title' => string 'My New Title' (length=12)
  'post' => string 'This is a blog post' (length=19)
  'date' => string '2009-07-01 00:00:00' (length=19)
  'timestamp' => string '2009-07-02 16:12:30' (length=19)

Eureka! We managed to get our data and, more importantly, completely configure a Model Magento.

Basic Model Operations

Magento All Models of the heirs of Varien_Object class. This lesson is part of the Magento system library and not part of Magento core module. You can object

lib/Varien/Object.php

Models Magento store their data in a protected _data property. Varian_Object the class gives us a number of methods we can use this data to retrieve. You’ve already seen GetData, which will return array of key / value pairs. This method can also be passed a string key to obtain a specific area.

$model->getData();
$model->getData('title');

There is also a getOrigData method that will return the data model as it was when the object was initially populated, (working with the protected _origData method).

$model->getOrigData();
$model->getOrigData('title');

The Varien_Object also carries some special methods using magic __call method of PHP. You can, set, off, or look for the existence of a property using a method that starts with the word get, set, or is disabled and is followed by the camel-cased name of a house.

$model->getBlogpostId();
$model->setBlogpostId(25);
$model->unsetBlogpostId();
if($model->hasBlogpostId()){...}

For this reason, you want your database column name with lowercase letters and underlined the use of separate characters. More recent versions of Magento have depreciated this syntax in favor of the implementation of the PHP interface Array Access

$id = $model->['blogpost_id'];
$model->['blogpost_id'] = 25;
 //etc...

That said, you’re probably both techniques used in the Magento code base to see, as well as third party extensions.

CRUD, the Magento Way

Magento models support basic create, read, update and delete CRUD functionality with the loading, storage and disposal methods. You’ve already seen the load method in action. Then along a single parameter, the load method will return a record whose ID field (in the source model) corresponds to the passed in value.

$blogpost->load(1);

The Save method will allow you to both INSERT a new model in the database, or an existing UPDATE. Add the following method to the Controller

public function createNewPostAction() {
    $blogpost = Mage::getModel('weblog/blogpost');
    $blogpost->setTitle('Code Post!');
    $blogpost->setPost('This post was created from code!');
    $blogpost->save();
    echo 'post created';
}

and execute controller action by loading the following URL

http://example.com/weblog/index/createNewPost

You will see an additional item stored in your database table. Then try the following to edit your post

public function editFirstPostAction() {
    $blogpost = Mage::getModel('weblog/blogpost');
    $blogpost->load(1);
    $blogpost->setTitle("The First post!");
    $blogpost->save();
    echo 'post edited';
}

And finally, your message is very similar syntax.

public function deleteFirstPostAction() {
    $blogpost = Mage::getModel('weblog/blogpost');
    $blogpost->load(1);
    $blogpost->delete();
    echo 'post removed';
}

Model Collections

So, with a single model is useful, but sometimes we want to list the models of intervention. Instead of returning a simple set of models, each model type Magento has a unique collection of objects associated with it. These objects implement the PHP IteratorAggregate and Countable interfaces, which means they can be passed to the count function, and are used for each constructs.

We cover collections in its entirety in a later article, but for now let’s look at basic setup and use. Add the following action method to your controller, and load it into your browser.

public function showAllBlogPostsAction() {
    $posts = Mage::getModel('weblog/blogpost')->getCollection();
    foreach($posts as $blog_post){
        echo '<h3>'.$blog_post->getTitle().'</h3>';
        echo nl2br($blog_post->getPost());
    }
}

Load URL action,

http://example.com/weblog/index/showAllBlogPosts

and you have a (now) known exception.

Warning: include(Alanstormdotcom/Weblog/Model/Mysql4/Blogpost/Collection.php) [function.include]: failed to open stream

You’re not surprised, are you? We need a PHP class file that defines our collection blog post to add. Each model has a protected property named _resourceCollectionName that a URI used to identify our collection contains.

  protected '_resourceCollectionName' => string 'weblog/blogpost_collection'

By default, this is the same URI that is used to our resource model, called “_collection” to the end. Magento believes Collections section of the source, so this URI is converted into the name of the class

Alanstorm_Weblog_Model_Mysql4_Blogpost_Collection

Add the following PHP Class at the following location

File: app/code/local/Alanstormdotcom/Weblog/Model/Mysql4/Blogpost/Collection.php

class Alanstormdotcom_Weblog_Model_Mysql4_Blogpost_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
    protected function _construct()
    {
            $this->_init('weblog/blogpost');
    }
}

As with our other classes, we have our collection in the Specimen URI init. (Blog / blog post). Rerun Action Controller, and you have to post information to be seen.

Wrapup and a Quick Note on Core Magento Models

Congratulations, you’ve created and configured your first Magento Model. In a later article we’ll look at Magento’s Entity Attribute Value Models (EFSA), which expand on what we learned here.There is also a small lie, we had more to tell. In this article we show that all basic Magento Models inherit Mage_Core_Model_Abstract. It’s not 100% true. This abstract method has not existed for Magento’s life, and there are many models in the system directly from Varien_Object inherit. Although this does not affect the models that you create is something you want to know the code to work with Magento.

(Based on Alanstorm Turtorial)

Comments

Leave a Reply

Your email address will not be published.

four × three =

Security Code: