Extended Content Management - Content Objects

Summary

  • Phenotype operates page orientied, but for any content, not limited to just one specific page, Phenotype additionally offers content objects
  • Your editors can create, edit, update records of those objects just like you would do with any form based database backend
  • For every type of content to be managed a specific customizable content class is created within config mode
  • Over 30 form-methods are present to customize the editing form
  • All data (content object records) is stored in one big table using the same database strucuture for all content types. You don’t have to care about data modelling at all.
  • You access the properties of your content object records with simple getter functions
  • The PhenotypePeer offers methods for fetching the records out of the database

Creating new Content Classes

Config → Content → Create new content object class creates a new Content class.

Following class scaffold is created for you:

<?php
/**
 * Name of your content object class
 *
 * @package phenotype
 * @subpackage application
 */
class PhenotypeContent_1 extends PhenotypeContent
{
  public $content_type = 1;
 
  // Remove comment slashes for multiple tabs
  // public $_blocks = Array("Config","Items");
  // public $_icons = Array("b_konfig.gif","b_items.gif");
 
  public function setDefaultProperties()
  {
      $this->set("bez","New Record");    
  }
 
  public function init($row,$block_nr=0) 
  { 
    parent::init($row,$block_nr); 
 
    // Customize your form with form_xyz methods
 
    $this->form_textfield("Name","bez",200);
 
    // If you have multiple tabs ... 
 
    /*switch ($block_nr)
    { 
      case 0:
      break;
    }*/
  }
 
  public function attachKeyFields()
  {
      // define keys here
    // $this->setKey1($this->get("propertyxy"));
  }
 
 
}

Tipp: Whenever creating a new content object class, as a default your editors won’t have the right to access the records of your new class! You must add the new content type to existing roles or assign the right to edit the new type per user.

Customizing Content Classes

The main purpose of content objects is to store any thinkable, reusable content. All data is stored internally, you don’t have to take care of that. Your obligation is to define the content entry mask of your content object class and provide logic to access the information, entered by your editors.

Customize Editing

Building the Entry Form

Within the init-method you can use →form_xy methods of the contentobject to provide your desired input fields.

Let’s demonstrate some form_methods, starting with form_textfield as included in the code scaffold. Generally you always have to determine a property name for every form element. On update of the content record editing form those properties are populated with the user input.

There are no naming restrictions for the properties of a content object. Only one property has a special meaning, so it always should be set. The property “bez” is always set as identifying name of a content object record (It’s an abbreviation for the german word “Bezeichnung” which is something like title, name, id).

You can access these properties later on with the common Phenotype getters like get(), getI(), getH(), getHBR(), getD()…

Text
  • →form_textfield
  • →form_textarea

form_textarea

  • →form_doubletextfield
  • →form_textfield_cluster
  • →form_ddtextfield_cluster
  • →form_richtext

form_richtext

Links
  • →form_link

form_link

Assets (Images and Documents)

Selecting Assets:

  • →form_document_selector
  • →form_image_selector

form_image_selector

Selections

Selecting a value out of an arbitrary selection:

  • →form_selectbox

form_selectbox

  • →form_doubleselectbox
  • →function form_multiselectbox

Providing a checkbox:

  • →function form_checkbox

Selecting a content object record:

  • →form_content_selectbox
Numbers
  • →form_number
Dates
  • function form_date
  • function form_datetime
Formatting the edit form

Following form methods don’t populate any property. They can be used to structure the entry form a little bit:

  • function form_headline
  • function form_newline
  • function form_comment
  • function form_editlink
  • function form_wrap
Sorting

This form methods provide gadgets to sort/order information:

  • function form_ddpositioner
  • function form_ddtextfield_cluster

See API Doc for all available form_xy methods.

Multiple Tabs

Content Entry forms can have multiple tabs. If you want to use multiple tabs, you must define them in your class. Just remove the appropriate commentary slashes in the code scaffold to activate a second tab:

// Remove comment slashes for multiple tabs
public $_blocks = Array("Config","Items");
public $_icons = Array("b_konfig.gif","b_items.gif");

Now you’ll have the tabs “Config” and “Items” on your editing form, representing tab/block 0 and 1.

To distinguish the tabs in your form initalization method use following switch/case construct:

switch ($block_nr)
{ 
  case 0:
   // your form_xy-methods for tab 0 ...
  break;
  case 0:
   // your from_xy-methods for tab 1 ...
  break;
}

Advanced techniques

Setting Default Values
  • →setDefaultProperties()

When creating a new record of a content class you might want to set some default properties. Do this by implementing the method →setDefaultProperties() and using the →set($property,$value) method within.

Using Entry Forms to Initiate Actions
  • →form_button
  • →buttonClicked

You may want to provide special actions (e.g. “Send Newsletter”) within your content entry form. Therefore you can place buttons with the form_button-method. To catch the activation you have to override the update-method and check for the press event with the method buttonClicked.

Dynamic Upload

Providing a drag & drop upload facility like that in mediabase:

  • function form_ddupload

More info here.

Script Editing

You may offer the opportunity to edit a file within your content entry form (just like script/template editing in config mode):

  • function form_script($input, $bez, $cols, $rows, $filename)
Integrating Page Components

It’s possible to use page components within content entry forms!

  • form_sequence

More info here.

Customize Update

You don’t need to pay attention on the form update. Everything is done automatically for you. But you always can override the update()-method to integrate your own update logic e.g. to perform special operations, when some content is edited.

Example 1 - Changing the name of your content records (on Update)

Think of a content type for storing address data. Your editor can enter a surname and a lastname. In case both informations are provided you want to change the name of the record to display the full name:

function update() 
{ 
  parent::update();
  if (($this->get("lastname")!="") AND ($this->get("surname")!=""))
  {
    $this->set("bez",$this->get("lastname") . ", " . $this->get("surname")); 
  }
  else
  {
    $this->set("bez",$this->get("lastname") . $this->get("surname")); 
  }
}

Example 2 - Checking user input and providing feedback (on Update)

  • →setErrorText($s)
  • →setInfoText($s)
  • →setAlertText($s)

Let’s assume we do have database to check the most probable gender for a given surname. We want to warn our editor in case of a likely mismatch:

function update() 
{ 
  parent::update();
  $gender = $this->checkGender4Surname($this->get("surname"));
 
  if ($gender)
  {
    if ($this->get("gender","m") != $gender)
    {
      $this->setAlertText("Most probable gender for ".$this->get("surname") ." is ".$gender.". Please recheck your entry.");
    }
  }
}

Advanced techniques

  • →setAlternateUpdateUrl

When storing a content entry form (pressing “Save”) Phenotype Backend either jumps back to the content records overview page or (if your form has muliple tabs) stays on the current active tab. Sometimes you may want to change that behaviour. Then implement the update-method and set a different URL with →setAlternateUpdateUrl($s);

Using Content Objects

Accessing Records

Initializing 1 Content Object

The most simple way to access a content object is to initialize it by it’s ID. Use the method isLoaded to check, if you provided a correct ID.

  • →__construct($id);
  • →load($id)
  • →isLoaded()
// Example 1:
$myCO = new PhenotypeContent_1(123);
// Example 2:
$myCO = new PhenotypeContent_1();
$myCO->load(123);

Tipp: When initalizing content objects the on-/offline status is meaningless!

Initializing a Number of Content Objects

If you have to select the eligible records first, you may do that with a query on the table content_data or use a static method of the PhenotypePeer.

  • $myCO→function init($row)
  • PhenotypePeer::getRecords($con_id,$status=true,$order=“dat_bez”,$_filter=array(),$limit)
  • PhenotypePeer::getByPermalink($con_id,$permalink,$status=true,$lng_id=1)
  • PhenotypePeer::getRecordsPaged($con_id,$page,$itemsperpage,$status=true,$order=“dat_bez”,$_filter=array())


Example 1:Direct database access:

global $myDB;
$sql = "SELECT * FROM content_data WHERE con_id = 1 AND dat_status = 1 ORDER BY dat_ikey1 DESC, dat_id DESC LIMIT 0,5"; 
$rs = $myDB->query($sql); 
while ($row=mysql_fetch_array($rs)) 
{ 
  $myCO = new PhenotypeContent_1(); 
  $myCO->init($row); 
  // do something
}

Example 2:Using the Peer:

$_objects = PhenotypePeer::getRecords(1,true,"dat_ikey1",array(),"0,5");
foreach ($_objects AS $myCO)
{
  // do something
}

If possible, you should always use the Peer and avoid direct database access. But sometimes you may need to do more complex quieries, so we described both possibilities here. For a full list of Peer methods check here

Indexing

All content records share the same database table: content_data. For proper record selection you need to reveal some of your internal content object properties, meaning mapping to key fields.

Let’s start with an example:

Here you have the form setup of a content type “project reference”:

...
$this->form_textfield("Project","title",200);
$this->form_content_selectbox("Customer","dat_id_customer",PhenotypeApplication::con_id_customer);
$this->form_date("Datum","date");
...

Now you want to map those properties to key fields for database (or Peer) selection:

function attachKeyFields()
{
  $this->setKey1($this->get("title"));
  $this->setKey2($this->get("date"));
  $this->setKey3($this->getI("dat_id_customer"));
}

From now on the table fields dat_key1, dat_ikey1, dat_key2, dat_ikey2, dat_key3, dat_ikey3 contain the property values and can be used for selections. Pretty simple …

It might be irritating at first, since the fixed table schema is an unusual approach and cause of this extra mapping step. But the benefit of that uniformaty is, that you don’t (have to) care about data storage/data manipulation at all, it’s just donefor you by the objects and the Peer (for data retrieval).

You got 12 key fields, 6 of type integer and 6 of type varchar. You must specify the type as additional parameter (e.g. setKey1($val,$type): setKey(124,DB_NUMBER) and setKey(“test”,DB_STRING)). If you omit the type both key fields are filled in.

Note: Default database modelling doesn’t have indexes on all key fields, since usally you simply don’t need all of them. If your application seems to be slow, check for suitability of the MySQL index settings.

Advanced techniques

Backend Thumbnails

Within the backend your content records are displayed with an default image. You might want to change that image. It’s done with the setThumbnail-method (to be called within attachKeyFields):

  function attachKeyFields()
  {
    ...
 
    $img_id = $this->getI("teaserimage_img_id");
    if ($img_id!=0)
    {
      $this->setThumbnail($img_id);
    }
  }
Positioning
  • function updatePosition()

There’s one special key field: dat_pos. Every time a content object is stored, the method updatePosition is called. If it returns a value, it’s stored into dat_pos.

Why is that special? Usally determing the position of one record, changes the position of others. So if you do use the field dat_pos, you must implement an algorithm to set the dat_pos field of all relevant content_records. This method is the place for it.

Data Tables

Sometimes 12 key fields just aren’t enough, or you need to have a specific table with specific field names (e.g. for integrating with other systems). Then you can map records & properties to an external table. More infos here (currently german only).

Creating New Records

  • function addnew($usr_id=-1)
  • function store()

New content objects are usally created within the backend by pressing the “Create New Record” button. But it’s also possible to create new records within a script, e.g. to store guest book entries.

A new object is created by calling →addnew() and stored afterwards with the method →store(). When calling →addNew() you can assign a user, if you omit that parameter the currently logged in user will be taken (only working in backend of course).

There are some special users (if you installed PT_CORE or PT_DEMO):

usr_idname
1System
2Importer
3WWW
  • function changeUserStatus($usr_id, $time = ””)

With the method changeUserStatus you can specific set the information about last change of a content record. The method store() doesn’t change this information, since script based changes usally shell not delete the information about last “human” access.

Continue ...

You think Phenotype Wiki/Documentation could be better?
We too. Please contribute: Edit this page

Bookmark and Share