tags:

views:

359

answers:

3

I have a configurable report. For each field that can be included on the report, there's a key (stored in report preferences), a label, potentially an access level, and a SQL descriptor -- something like foo as my_foo.

In a Java app, I would create a class called ReportField with each of the properties listed above. I'd use a private constructor, and list each of the fields in the class like this:

public final static ReportField FOO = new ReportField('foo', 'Foo', 1, 'foo as my_foo');

I'd probably create a static array of all of the fields, add a static method that allows a field to be looked up by key, and so forth. Then in other places I could write code like:

List<String> selectFields = new ArrayList<String>();
for (ReportPref pref : reportPrefs) {
    selectFields.add(ReportField.getByKey(pref.getField()).getFieldSql());
}

Apologies for the Java code, but hopefully you get my point.

Is there an idiomatic way to solve the same problem in PHP? I can think of a number of solutions -- nested associative arrays will do the trick -- but I'd like to avoid a hackish solution.

+4  A: 

Why not create objects in PHP like you would in Java?

class ReportField {
  private $key;

  public __construct($key, $label, $access_level, $sql) {
    $this->key = $key;
    ...
  }

  public getKey() { return $this->key; }

  ...
}

$fields = array(
  new ReportField(...),
  new ReportField(...),
);

foreach ($fields as $field) {
  echo $field->getKey();
}

and so on.

Other than that, associative arrays can be just fine.

cletus
A: 

I don't particularly see any reason to do it very differently in PHP than in Java. Most of the same OOP functionality is there, and you may as well stick with what's working for you.

It'd look more like

$foo = new ReportField('foo', 'Foo', 1, 'foo as my_foo');

$selectFields = array();
foreach($reportPrefs as $pref)
    $selectFields[] = ReportField::getByKey($pref->getField())->getFieldSql();

but the theory remains intact.

chaos
+1  A: 

I don't know Java super well, but you can do most of that - just have to do it differently, unless I misunderstand your question.

Data members on PHP classes can't have runtime-calculated values, such as new object instances. So, this would not work

class ReportField
{
  public static $foo = new ReportField()
}

Note: final properties are not allowed except on methods

It's actually really curious to me that you're making one class responsible for two things - an object blueprint AND static storage for instances of itself as well.

Anyway, here's what I think your code would look like in PHP

<?php

class ReportField
{
  public static $store = array();

  private
    $key,
    $label,
    $accessLevel,
    $sql;

  private function __construct( $key, $label, $accessLevel, $sql )
  {
    $this->key = $key;
    $this->label = $label;
    $this->accessLevel = $accessLevel;
    $this->sql = $sql;
  }

  public static function initializeStore()
  {
    if ( empty( self::$store ) )
    {
      self::$store['foo'] = new self( 'foo', 'Foo', 1, 'foo as my_foo' );
      // repeat
    }
  }

  public static function getByKey( $key )
  {
    if ( empty( self::$store ) )
    {
      self::initializeStore();
    }
    if ( isset( self::$store[$key] ) )
    {
      return self::$store[$key];
    }
    throw new Exception( __CLASS__ . " instance identified by key $key not found" );
  }

  public function getFieldSql()
  {
    return $this->sql;
  }
}

// Usage
$selectFields = array();
foreach ( $reportPrefs as $pref )
{
  $selectFields[] = ReportField::getByKey( $pref->getField() )->getFieldSql();
}
Peter Bailey
After reading the earlier answers, this is the approach that seemed logical to me. A static method for access that initializes the collection lazily seems to be the most logical approach.As for why I'd take this approach, it's a nice way to create a set of options with multiple properties that is immutable except by the programmer. Normally you'd just use a database table for this sort of thing, but because of the way it's tied into the app, making it part of the code rather than part of the data makes more sense. (Even though it's data.)
Rafe