Step 2 - Database, Backend, Languages

Structure of a component in zip-package

After the basics we want to achieve more.

We want to have a fully functioning component with a backend for adding, editing and deleting objects and we want to have separated language files, of course, to have the possibility to localise our component. In general this is not complicated but we have to create many files and it is easy to get lost in folders, filenames and methods.

I want you to start with a few screenshots to give you an idea of what I am talking about :)

Cocoate Real Estate (CRE) Version 0.0.2 Screenshots

The component consists more or less of two components. One is responsible for the Frontend (site) and one for the administration area (admin). It is still a simple component without eye candy, ACL, additional JavaScript and all the other fancy stuff but it will be a robust foundation to discover more.

Site

For the moment we only want to have the possibility to create a link to one object (Figure 1). Later on we will enhance that.

cocoate.com/node/10197

Figure 1: One listing in the frontend

Admin

To be able to create the menulink for the site we need a menu item type (Figure 2).

cocoate.com/node/10197

Figure 2: Menu item type

After we choose the menu item Type we have to select the object we want to present. There will be an option list consisting of different fields fetched from the database. This step is important because in our first try we just wrote the text in an xml file. Here the option list is created dynamically, depending on the content of our database table (Figure 3).

cocoate.com/node/10197

Figure 3: Dynamic parameters

To add, edit and delete objects we need an overview page like in Figure 4. We need a headline, a toolbar with Icons, checkboxes and of course content.

cocoate.com/node/10197

Figure 4: Backend Table

When you click on the title link you should be directed to an edit form. In this edit form, we need a different toolbar, fields and of course labels and description to help the user to understand what should be done (Figure 5). The form should appear too when the New icon is clicked! After saving, there should be a message for the user.

cocoate.com/node/10197

Figure 5: Edit form

In the case of edit, it should be possible to tick the checkbox of the row and click the icon edit. If nothing is checked and the edit icon is clicked there should be a message(Figure 6)

cocoate.com/node/10197

Figure 6: Message that nothing is checked

And last but not least it should be possible to delete the freshly added object.

CRE Version 0.0.2 Files

In this step we need a lot of additional files. If you still work with a "simple" text editor it can become a bit confusing. I propose that you install the example component (link below the chapter) and go through all the files.

It is important to keep in mind that the folder structure in the installation package differs from the folder structure in in the Joomla! CMS.

Please take your time and have a look at the folder structure in the ZIP file (Figure 7) and the file structure in the CMS after installing (Figure 8).

cocoate.com/node/10197

Figure 7: Folder structure in installation package

cocoate.com/node/10197

Figure 8: Folder structure in Joomla! CMS

Database Table cocoaterealestate_objects

We need to store our listings somewhere and I had a chicken and egg problem when I wrote the chapter. Of course I wrote two files for installing and uninstalling the table cocoaterealestate_objects (Listing 1, Listing 2) but initially I build the table manually using phpMyAdmin.

After the code was complete it was possible to install the component an the two file are called from the installing and uninstalling process.

The files contain pure SQL commands and consequently the extension is .sql. To keep it "simple" I structured the table in a simple way with fields for title, image, description, city, zip, country and price. Keep in mind that the drop command at the top of the install file can accidently delete existing data. Depending on your update plans it can be useful or dangerous :).

DROP TABLE IF EXISTS `#__cocoaterealestate_objects`;
 
CREATE TABLE `#__cocoaterealestate_objects` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `uid` int(10) unsigned NOT NULL DEFAULT '0',
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `published` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `ordering` int(10) unsigned NOT NULL DEFAULT '0',
  `image` varchar(255)  NOT NULL DEFAULT '',
  `meta_descr` varchar(250) DEFAULT NULL,
  `meta_keys` varchar(250) DEFAULT NULL,
  `title` varchar(200) NOT NULL DEFAULT '',
  `description` text,
  `city` varchar(100) NOT NULL DEFAULT '',
  `zip` varchar(50) NOT NULL DEFAULT '',
  `country` varchar(100) NOT NULL DEFAULT '',
  `price` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;
 
INSERT INTO `#__cocoaterealestate_objects`  VALUES(1, 42, '2011-11-29 15:39:10', 1, 0, 'http://farm4.staticflickr.com/3100/2724105775_4d039b4127.jpg', NULL, NULL, 'First House', 'Sed id leo metus, ut mollis mi. Etiam malesuada ornare felis, vel imperdiet eros cursus sollicitudin. Nulla viverra, neque sodales porttitor accumsan, felis purus varius libero, eu posuere odio risus ac nisl. Proin quis eros ipsum, sit amet pretium eros? Proin at purus cras amet.\r\n', 'Fitou', '11510', 'France', 85000);

INSERT INTO `#__cocoaterealestate_objects` VALUES(2, 42, '2011-11-29 15:39:10', 1, 0, 'http://farm6.staticflickr.com/5298/5489897350_eaf091d99b.jpg', NULL, NULL, 'Second House', 'bumsclabe laber Sed id leo metus, ut mollis mi. Etiam malesuada ornare felis, vel imperdiet eros cursus sollicitudin. Nulla viverra, neque sodales porttitor accumsan, felis purus varius libero, eu posuere odio risus ac nisl. Proin quis eros ipsum, sit amet pretium eros? Proin at purus cras amet.\r\n', 'Fitou', '11510', 'France', 100000);

Listing 1: /administrator/components/com_cocoaterealestate/sql/install.mysql.utf8.sql

DROP TABLE IF EXISTS `#__cocoaterealestate_objects`;

Listing 2: /administrator/components/com_cocoaterealestate/sql/uninstall.mysql.utf8.sql

Models, Tables, Fields, Language Files

Beside the database table itself we need a table class and various models to manage the needs of our component.

Table class

The table class lives in the administration area of the CMS in /administrator/components/com_cocoate_realestate/tables/objects.php (Listing 3). You define as many tables as you need. The name of the class consists of a prefix (CocoateRealEstateTable) and of the virtual name of the table (Objects). An instance of this class represent a row in the db table which means one house listing.

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.database.table');
class CocoateRealEstateTableObjects extends JTable
{
  var $id = null;
  var $title = null;
  var $city = null;
  var $price = null;
  var $published = 0;

  function __construct(&$db)
  {
    parent::__construct('#__cocoaterealestate_objects', 'id', $db);
  }
}
?>

Listing 3: /administrator/components/com_cocoate_realestate/tables/objects.php

Model - Frontend

The cool thing is that we can create a link for a single object (Figure 1). Therefore we need a model for ONE row (one object/house listing). It is important to distinguish between a single house listing and list/table of house listings. In Joomla! we call the model file in a singular way, if we want to have ONE item (object.php) and in the plural way if we want a list of item (objects.php). The name of the model has to be similar to the name of the view folder.

In our case the name of the view folder is object so we call the model file object.php too (Listing 4).

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
jimport('joomla.application.component.modelitem');
class CocoateRealEstateModelObject extends JModelItem
{
  protected $item;
  public function getItem()
  {
    if (!isset($this->item)) {
      $id = JRequest::getInt('id');
      // Get a TableObject instance
      $table = $this->getTable('Objects', 'CocoateRealEstateTable');
      // Load the object
      $table->load($id);
      // Assign the data
      $this->item['id'] = $table->id;
      $this->item['image'] = $table->image;
      $this->item['title'] = $table->title;
      $this->item['city'] = $table->city;
      $this->item['zip'] = $table->zip;
      $this->item['country'] = $table->country;
      $this->item['price'] = $table->price;
    }
    return $this->item;
  }
}
?>

Listing 4: /components/com_cocoate_realestate/models/object.php

Model/Field - Backend

The view related to the object model needs a kind of relationsship to this model. This is done by an entry in an xml file called /components/cocoaterealestate/views/object/tmpl/default.xml (Listing 5). The important attribute is addfieldpath. The WORDS IN CAPITAL LETTERS are variables for language files.

<?xml version="1.0" encoding="utf-8"?>
<metadata>
  <layout title="COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_TITLE">
    <message>COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_DESC</message>
  </layout>
  <fields name="request" addfieldpath="/administrator/components/com_cocoaterealestate/models/fields">
    <fieldset name="request">
      <field
        name="id"
        type="object"
        extension="com_cocoaterealestate"
        label="COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_LABEL"
        description="COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_LABEL"
        required="true"
        />
    </fieldset>
  </fields>
</metadata>

Listing 5: /components/cocoaterealestate/views/object/tmpl/default.xml

Language files

Language files have nothing to do with models but I want mention them now because we need them and I already have used language variables (THE ONES WITH CAPITAL LETTERS)

The language file for the frontend would be /language/en-GB/en-GB.com_cocoaterealestate.ini. The name for the German language file would be /language/de-DE/de-DE.com_cocoaterealestate.ini. At the moment we need no textstrings for the frontend.

The two language files for the backend are stored in the folder /administrator/language/en-GB/. One is called en-GB.com_cocoaterealestate.sys.ini (Listing 6) and the other one is called en-GB.com_cocoaterealestate.ini (Listing 7). The sys.ini file is will be used during the installation process, in the extension manager and in the menus, another language. It contains a lot less translation strings and this file is loaded in scenarios where the loaded component is not com_cocoaterealestate itself, but minimal translation is still needed.

COM_COCOATEREALESTATE="Cocoate Real Estate"
COM_COCOATEREALESTATE_DESCRIPTION="House listings on your website."
COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_TITLE="Single object"
COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_DESC="This view displays a single object"
COM_COCOATEREALESTATE_MENU="Cocoate Real Estate"

Listing 6: /administratorlanguage/en-GB/en-GB.com_cocoaterealestate.sys.ini

COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_DESC="This object will be displayed"
COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_LABEL="Object"
COM_COCOATEREALESTATE_OBJECT_HEADING_ID="ID"
COM_COCOATEREALESTATE_OBJECT_HEADING_OBJECT="Object"
COM_COCOATEREALESTATE_OBJECT_HEADING_TITLE="Title"
COM_COCOATEREALESTATE_OBJECT_HEADING_COUNTRY="Country"
COM_COCOATEREALESTATE_OBJECT_HEADING_CITY="City"
COM_COCOATEREALESTATE_OBJECT_HEADING_IMAGE="Image"
COM_COCOATEREALESTATE_OBJECT_HEADING_ZIP="ZIP"
COM_COCOATEREALESTATE_OBJECT_HEADING_PRICE="Price"
COM_COCOATEREALESTATE_MANAGER_OBJECTS="CocoateRealEstate manager"
COM_COCOATEREALESTATE_MANAGER_OBJECT_NEW="CocoateRealEstate manager: New Object"
COM_COCOATEREALESTATE_MANAGER_OBJECT_EDIT="CocoateRealEstate manager: Edit Object"
COM_COCOATEREALESTATE_N_ITEMS_DELETED_1="One object deleted"
COM_COCOATEREALESTATE_N_ITEMS_DELETED_MORE="%d objects deleted"
COM_COCOATEREALESTATE_OBJECT_DETAILS="Object Details"
COM_COCOATEREALESTATE_OBJECT_FIELD_TITLE_LABEL="Title"
COM_COCOATEREALESTATE_OBJECT_FIELD_TITLE_DESC="Title"
COM_COCOATEREALESTATE_OBJECT_FIELD_IMAGE_LABEL="Image"
COM_COCOATEREALESTATE_OBJECT_FIELD_IMAGE_DESC="Please paste a URL"
COM_COCOATEREALESTATE_OBJECT_FIELD_ZIP_LABEL="ZIP"
COM_COCOATEREALESTATE_OBJECT_FIELD_ZIP_DESC="Enter ZIP code"
COM_COCOATEREALESTATE_OBJECT_FIELD_CITY_LABEL="City"
COM_COCOATEREALESTATE_OBJECT_FIELD_CITY_DESC="City"
COM_COCOATEREALESTATE_OBJECT_FIELD_COUNTRY_LABEL="Country"
COM_COCOATEREALESTATE_OBJECT_FIELD_COUNTRY_DESC="Country"
COM_COCOATEREALESTATE_OBJECT_FIELD_PRICE_LABEL="Price"
COM_COCOATEREALESTATE_OBJECT_FIELD_PRICE_DESC="Enter price"

Listing 7: /administratorlanguage/en-GB/en-GB.com_cocoaterealestate.ini

Models, Fields and Forms - Backend

The parameter field for choosing the right object for the menu link needs a relationship to the model. Therefore we create a folder fields inside of the models folder. In this folder we store the structure of the parameter field and call it object.php (Listing 8).

<?php
defined('_JEXEC') or die;
jimport('joomla.form.helper');
JFormHelper::loadFieldClass('list');
class JFormFieldObject extends JFormFieldList
{
  protected $type = 'Object';
  protected function getOptions()
  {
    $db = JFactory::getDBO();
    $query = $db->getQuery(true);
    $query->select('id,title,price,city');
    $query->from('#__cocoaterealestate_objects');
    $db->setQuery((string)$query);
    $titles = $db->loadObjectList();
    $options = array();
    if($titles){
      foreach($titles as $title)
      {
        $options[] = JHtml::_('select.option', $title->id, $title->id.' '.$title->city.' '.$title->title.' '.money_format('%i', $title->price));
      }
    }
    $options = array_merge(parent::getOptions(), $options);
    return $options;
  }
}

Listing 8: /administrator/components/com_cocoate_realestate/models/fields/object.php

In the backend we have an overview page (Figure 4) and a form for to edit and add a single object (Figure 5). For that reason we need two models - object.php and objects.php (Listing 9 and Listing 10)

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.modeladmin');
class CocoateRealEstateModelObject extends JModelAdmin
{
  public function getForm($data = array(), $loadData = true)
  {
    // Get the form.
    $form = $this->loadForm('com_cocoaterealestate.object', 'object', array('control' => 'jform', 'load_data' => $loadData));
    return $form;
  }
 
  protected function loadFormData()
  {
    // Check the session for previously entered form data.
    $data = JFactory::getApplication()->getUserState('com_cocoaterealestate.edit.object.data', array());
    if(empty($data)){
      $data = $this->getItem();
    }
    return $data;
  }
 
  public function getTable($name = 'Objects', $prefix = 'CocoateRealEstateTable', $options = array())
  {
    return parent::getTable($name, $prefix, $options);
  }
}

Listing 9: /administrator/components/com_cocoate_realestate/models/object.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.modellist');
class CocoateRealEstateModelObjects extends JModelList
{
  protected function getListQuery()
  {
    // Create a new query object.
    $db = JFactory::getDBO();
    $query = $db->getQuery(true);
 
    // Select some fields
    $query->select('id,title,city,country,price');
 
    // From the realestate table
    $query->from('#__cocoaterealestate_objects');
    return $query;
  }
}
?>

Listing 10: /administrator/components/com_cocoate_realestate/models/objects.php

To add an object/listing we need a form. Forms are located in the model folder too. The extension for form files is .xml and the name is the same than the name of the view where the form is needed. In our case is the name again object (Listing 11)

<?xml version="1.0" encoding="utf-8"?>
<form>
  <fieldset>
    <field
      name="id"
      type="hidden"
    />
    <field
      name="title"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_TITLE_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_TITLE_DESC"
      size="40"
      class="inputbox"
      default=""
    />
    <field
      name="image"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_IMAGE_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_IMAGE_DESC"
      size="40"
      class="inputbox"
      default=""
    />
    <field
      name="zip"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_ZIP_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_ZIP_DESC"
      size="40"
      class="inputbox"
      default=""
    />
    <field
      name="city"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_CITY_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_CITY_DESC"
      size="40"
      class="inputbox"
      default=""
    />
    <field
      name="country"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_COUNTRY_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_COUNTRY_DESC"
      size="40"
      class="inputbox"
      default=""
    />
    <field
      name="price"
      type="text"
      label="COM_COCOATEREALESTATE_OBJECT_FIELD_PRICE_LABEL"
      description="COM_COCOATEREALESTATE_OBJECT_FIELD_PRICE_DESC"
      size="40"
      class="inputbox"
      default=""
    />
  </fieldset>
</form>

Listing 11: /administrator/components/com_cocoate_realestate/models/forms/objects.xml

Controllers

The controllers are necessary to decide what to do next. If you click the New icon to add a house listing, a controller has to find the right way what to do next. In total we use four controllers at the moment.

  • One for the frontend (/component/com_cocoaterealestate/controller.php - listing 12)
  • One generic controller with a default option (in our case objects) for the backend (/administrator/component/com_cocoaterealestate/controller.php - listing 13)
  • Two controllers for the backend for the list view (/administrator/component/com_cocoaterealestate/controllers/objects.php - listing 14) and for the single view (/administrator/component/com_cocoaterealestate/controllers/object.php - listing 15).

/component/com_cocoaterealestate/controller.php

This controller does nothing at the moment. It has simply to be there (Listing 12)

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.controller');
class CocoateRealEstateController extends JController
{
}

Listing 12: /administrator/component/com_cocoaterealestate/controller.php

/administrator/component/com_cocoaterealestate/controller.php

The controller has to be there too but in this case we have two views, so one of must be the default view. The controller sets the default view to objects.

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.controller');
class CocoateRealEstateController extends JController
{
  function display($cachable = false)
  {
    // Set default view if not set
    JRequest::setVar('view', JRequest::getCmd('view', 'objects'));
    parent::display($cachable);
  }
}
?>

Listing 13: /administrator/component/com_cocoaterealestate/controller.php

administrator/component/com_cocoaterealestate/controllers/objects.php

 

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.controlleradmin');
class CocoateRealEstateControllerObjects extends JControllerAdmin
{
  public function getModel($name = 'Object', $prefix = 'CocoateRealEstateModel')         {
    $model = parent::getModel($name, $prefix, array('ignore_request' => true));
    return $model;
  }
}

Listing 14 /administrator/component/com_cocoaterealestate/controllers/objects.php

administrator/component/com_cocoaterealestate/controllers/object.php

This controller has to be there but can remain empty.

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.controllerform');
class CocoateRealEstateControllerObject extends JControllerForm
{
}

Listing 15 /administrator/component/com_cocoaterealestate/controllers/object.php

Views in frontend and backend

In our example we have three views

  • The object view in the frontend (Figure 1) displaying a single object. It consists of three file
    /component/com_cocoaterealestate/views/object/view.html.php (Listing 16)
    /component/com_cocoaterealestate/views/object/tmpl/default.php (Listing 17)
    /component/com_cocoaterealestate/views/object/tmpl/default.xml (Listing 18) (I already mentioned that file above)
  • The objects view in the backend (Figure 4) displaying a list of objects/houses. It consists of five files
    /administrator/component/com_cocoaterealestate/views/objects/view.html.php (Listing 19)
    /administrator/component/com_cocoaterealestate/views/objects/tmpl/default.php (Listing 20)
    /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_body.php (Listing 21)
    /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_foot.php (Listing 22)
    /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_head.php (Listing 23)
  • The object view in the backend (Figure 5) displaying the form. It consists of two files
    /administrator/component/com_cocoaterealestate/views/object/view.html.php (Listing 24)
    /administrator/component/com_cocoaterealestate/views/object/tmpl/edit.php (Listing 25)

The structure of the views are very important. The view.html.php collects the data from the model and provide it as variables for the "real" template called default.php. The default.php is made for Designers and it is overridable by any Joomla! template (Read more in Chapter Template Overrides). It should contain only markup enriched with PHP variables.

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.view');
class CocoateRealEstateViewObject extends JView
{
  protected $item;  
  function display($tpl = null)
  {
    // Assign data to the view
    //$this->item = 'Cocoate Real Estate';
    $this->item = $this->get('item');
        
    // Display the view
    parent::display($tpl);
  }
}

Listing 16: /component/com_cocoaterealestate/views/object/view.html.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
?>
<h1><?php echo $this->item['title']; ?></h1>
<img src="<?php echo $this->item['image']; ?>">
<ul>
  <li>
  <?php echo $this->item['zip']; ?>
  <?php echo $this->item['city']; ?>,
  <?php echo $this->item['country']; ?>
  </li>
  <li>
  <strong><?php echo $this->item['price']; ?> €</strong>
  </li>
</ul>
<pre>
<?php
// uncomment the next line to see the array
// print_r($this->item); ?>
</pre>

Listing 17: /component/com_cocoaterealestate/views/object/tmpl/default.php

<?xml version="1.0" encoding="utf-8"?>
<metadata>
  <layout title="COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_TITLE">
    <message>COM_COCOATEREALESTATE_OBJECT_VIEW_DEFAULT_DESC</message>
  </layout>
  <fields name="request" addfieldpath="/administrator/components/com_cocoaterealestate/models/fields">
    <fieldset name="request">
      <field
        name="id"
        type="object"
        extension="com_cocoaterealestate"
        label="COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_LABEL"
        description="COM_COCOATEREALESTATE_OBJECT_FIELD_OBJECT_LABEL"
        required="true"
        />
    </fieldset>
  </fields>
</metadata>

Listing 18: /component/com_cocoaterealestate/views/object/tmpl/default.xml

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.view');
class CocoateRealEstateViewObjects extends JView
{
  function display($tpl = null)
  {
    // Get data from the model
    $items = $this->get('Items');
    $pagination = $this->get('Pagination');
 
    // Assign data to the view
    $this->items = $items;
    $this->pagination = $pagination;
                
    // Set the toolbar
    $this->addToolBar();
 
    // Display the template
    parent::display($tpl);
  }
        
  protected function addToolBar()
  {
    JToolBarHelper::title(JText::_('COM_COCOATEREALESTATE_MANAGER_OBJECTS'));
    JToolBarHelper::deleteListX('', 'objects.delete');
    JToolBarHelper::editListX('object.edit');
    JToolBarHelper::addNewX('object.add');
  }      
}
?>

Listing 19: /administrator/component/com_cocoaterealestate/views/objects/view.html.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
JHtml::_('behavior.tooltip');
?>
<form action="<?php echo JRoute::_('index.php?option=com_cocoaterealestate'); ?>" method="post" name="adminForm">
  <table class="adminlist">
    <thead><?php echo $this->loadTemplate('head');?></thead>
    <tfoot><?php echo $this->loadTemplate('foot');?></tfoot>
    <tbody><?php echo $this->loadTemplate('body');?></tbody>
  </table>
  <div>
    <input type="hidden" name="task" value="" />
    <input type="hidden" name="boxchecked" value="0" />
    <?php echo JHtml::_('form.token'); ?>
  </div>     
</form>

Listing 20: /administrator/component/com_cocoaterealestate/views/objects/tmpl/default.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
?>
<?php foreach($this->items as $i => $item): ?>
  <tr class="row<?php echo $i % 2; ?>">
  <td><?php echo $item->id; ?></td>
  <td><?php echo JHtml::_('grid.id', $i, $item->id); ?></td>
  <td>
  <a href="<?php echo JRoute::_('index.php?option=com_cocoaterealestate&task=object.edit&id=' . $item->id); ?>">
  <?php echo $item->title; ?>
  </a>
  </td>
  <td><?php echo $item->city; ?></td>
  <td><?php echo $item->country; ?></td>
  <td><?php echo $item->price; ?></td>
  </tr>
<?php endforeach; ?>

Listing 21: /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_body.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
?>
<tr>
  <td colspan="6"><?php echo $this->pagination->getListFooter(); ?></td>
</tr>

Listing 22: /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_foot.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
?>
<tr>
  <th width="5">
  <?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_HEADING_ID'); ?>
  </th>
  <th width="20">
  <input type="checkbox" name="toggle" value="" onclick="checkAll(<?php echo count($this->items); ?>);" />
  </th>
  <th>
  <?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_HEADING_TITLE'); ?>
  </th>
  <th>
  <?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_HEADING_CITY'); ?>
  </th>
  <th>
  <?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_HEADING_COUNTRY'); ?>
  </th>
  <th>
  <?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_HEADING_PRICE'); ?>
  </th>
</tr>

Listing 23: /administrator/component/com_cocoaterealestate/views/objects/tmpl/default_head.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
jimport('joomla.application.component.view');
class CocoateRealEstateViewObject extends JView
{
  public function display($tpl = null)
  {
    // get the Data
    $form = $this->get('Form');
    $item = $this->get('Item');
    
    // Assign the Data
    $this->form = $form;
    $this->item = $item;
 
    // Set the toolbar
    $this->addToolBar();
 
    // Display the template
    parent::display($tpl);
  }
 
  protected function addToolBar()
  {
    JRequest::setVar('hidemainmenu', true);
    $isNew = ($this->item->id == 0);
    JToolBarHelper::title($isNew ? JText::_('COM_COCOATEREALESTATE_MANAGER_OBJECT_NEW') : JText::_('COM_COCOATEREALESTATE_MANAGER_OBJECT_EDIT'));
    JToolBarHelper::save('object.save');
    JToolBarHelper::cancel('object.cancel', $isNew ? 'JTOOLBAR_CANCEL' : 'JTOOLBAR_CLOSE');
  }
}

Listing 24: /administrator/component/com_cocoaterealestate/views/object/view.html.php

<?php
// No direct access to this file
defined('_JEXEC') or die;
JHtml::_('behavior.tooltip');
?>
<form action="<?php echo JRoute::_('index.php?option=com_cocoaterealestate&layout=edit&id='.(int) $this->item->id); ?>"
  method="post" name="adminForm" id="object-form">
  <fieldset class="adminform">
    <legend><?php echo JText::_('COM_COCOATEREALESTATE_OBJECT_DETAILS'); ?></legend>
    <ul class="adminformlist">
      <?php foreach($this->form->getFieldset() as $field): ?>
        <li><?php echo $field->label;echo $field->input;?></li>
      <?php endforeach; ?>
    </ul>
  </fieldset>
  <div>
    <input type="hidden" name="task" value="object.edit" />
      <?php echo JHtml::_('form.token'); ?>
  </div>
</form>

Listing 25: /administrator/component/com_cocoaterealestate/views/object/tmpl/edit.php
 

AttachmentSize
com_coco_real_estate-0.0.2.zip42.68 KB

Comments

It's rather hard to follow the examples when it repeatedly switch back and forth between the front end and back end.

Recommend focusing the tutorial on the front end, when that is complete, focus on the back end.

First of all - thanks for posting this tutorial it is one of the best I have came across so far. It really helped me a lot. I have also found a one thing which didn't work correctly. It is a functionality which allows you to select the object of a house and assign it to the menu item. When someone selects "Menu item type -> Single Object" page breaks up and you are not allowed to select the object of a house.

In my case - this was caused by the money_format('%i', $title->price) function in object.php(Listing 8). I don't know if it had something to do with my php version (5.4.4) or some other server settings, but after removing a call of money_format(..) function everything works perfectly.

Great tut! Only one thing: there is TYPO, where you have written Listing 11: /administrator/components/com_cocoate_realestate/models/forms/objects.xml it should be Listing 11: /administrator/components/com_cocoate_realestate/models/forms/object.xml :) Otherwise you will see nothing in edition or create form. Cheers!