Assign select id, name to a straight array - but keep keys?

I just noticed a second time I needed to do the equivalent of this today…


$colors = $PDO->query("select id, color_name from colors order by color_name");
// PDO set to return objects - in this case

// reassign each result to an array
foreach( $colors as $key=>$val ){
$cols[$key] = $val ;
}

From a table that looks like this:


colors
====
id | color_name | hue | alpha
-------------------------
1, white, 34, 75

So that I can end up with a straight array:


$colors[3] = 'black';
$colors[19] = 'blue';
$colors[5] = 'indigo';
$colors[2] = 'red';
$colors[1] = 'white';

Which then gets passed as is to another class.

So my question is: Is there a more efficient way of doing this?

I can use any switch on PDO, I just generally return objects by nature.

More efficient? Probably not Cups. :slight_smile:


<?php
$colours = array(
  array(
    'id'      => 1,
    'colour'  => 'red'
  ),
  array(
    'id'      => 2,
    'colour'  => 'green'
  ),
  array(
    'id'      => 3,
    'colour'  => 'blue'
  ),
);

class Thingy extends ArrayIterator
{
  public function key(){
    $current = parent::current();
    return $current['id'];
  }
  
  public function current(){
    $current = parent::current();
    return $current['colour'];
  }
}

foreach(new Thingy($colours) as $id => $value){
  printf('%d => %s' . PHP_EOL, $id, $value);
}

/*
  1 => red
  2 => green
  3 => blue
*/

I would say that’s the way to do it, and I don’t think two short lines is very inefficient.

(2 lines when using the shorthand)


foreach($colors as $key => $val)
    $cols[$key] = $val;

hmm, can do this , but cannot quite see the point of it …


select concat('$arr[', id, ']="', color_name,'";') as array from colors;

Wouldn’t using some kind of native array_map() function be better? (if the result from PDO was say, an assoc array instead of a bunch of object).

By the way this goes on to create the guts of a html SELECT box - at the moment I just pass it an array - thinks - aha, perhaps I should train it to be passed an array of PDO objects instead/as well.

I don’t think anything is going to be as clean, and as obvious, as what you’re doing already. You could implement some funky (read: magic) SPL stuff with iterators, ArrayAccess and a dash of PDO::FETCH_INTO but I think it would be counter-productive.

You could write a simple function like


function getListData($table, $key, $value)
{
  $data = $PDO->query("$key, $value from $table order by $value");
  // PDO set to return objects - in this case

  // reassign each result to an array
  foreach( $data as $k=>$val ){
    $cols[$k] = $val ;
  } 
  return $cols;
}

$data=getListData('colours', 'id', 'color_name');

That way you don’t have to write the whole thing over and over again.

(inspired by Yii’s CHtml::listData() function)

Yes, thanks for casting eyes over it, I was thinking that I might be missing something.

In effect the class I pass it to ALSO iterates through the array looking for a “match” to echo selected=selected in the htm select box option.

I would be as well extending that to take an assoc array and do the 2 operations together - which did not dawn on me till I started replying to my own thread…

For instance, rather messy but you get the idea… PDO:FETCH_INTO ‘AssociativeColour’.


<<?php
class AssociativeColour extends ArrayObject
{
  protected
    $id       = null,
    $colour   = null;
  
  public function __set($name, $value){
    
    if('id' === $name){
      $this->setId($value);
    }
    
    if('colour' === $name){
      $this->setColour($value);
    }
    
    $this->bind();
  }
  
  private function setId($id){
    $this->id = $id;
  }
  
  private function setColour($colour){
    $this->colour = $colour;
  }
  
  private function bind(){
    
    $hasId = null !== $this->id;
    
    $hasColour = null !== $this->colour;
    
    if($hasId && $hasColour){
      parent::offsetSet($this->id, $this->colour);
    }
    
  }
}

Urgh.

I must say, it does not get more optimal than this.

@Anthony, thanks - I like reading your Iterator responses, it always opens my eyes even if you think they are urgh.

Here is the method in the class which I think will have to override (part of the Xajax library, btw).


/*
	Class: clsSelect
	
	A <xajaxControlContainer> derived class that assists in the construction
	of an HTML select control.
	
	This control can only accept <clsOption> controls as children.
*/
class clsSelect extends xajaxControlContainer
{
	/*
	Function: clsSelect
	
	Construct and initialize an instance of the class.  See <xajaxControlContainer>
	for details regarding the aConfiguration parameter.
    */
function clsSelect($aConfiguration=array())
{
	xajaxControlContainer::xajaxControlContainer('select', $aConfiguration);
}

/*
Function: addOption
	
Used to add a single option to the options list.
sValue - (string):  The value that is returned as the form value
when this option is the selected option.
sText - (string):  The text that is displayed in the select box when
this option is the selected option.
*/
function addOption($sValue, $sText)
{
	$optionNew =& new clsOption();
	$optionNew->setValue($sValue);
	$optionNew->setText($sText);
	$this->addChild($optionNew);
}
	
/*
Function: addOptions
	
Used to add a list of options.
	
aOptions - (associative array):  A list of key/value pairs that will
be passed to <clsSelect->addOption>.
*/
function addOptions($aOptions, $aFields=array())
{
	if (0 == count($aFields))
		foreach ($aOptions as $sValue => $sText)
			$this->addOption($sValue, $sText);
	else if (1 < count($aFields))
		foreach ($aOptions as $aOption)
			$this->addOption($aOption[$aFields[0]], $aOption[$aFields[1]]);
	else
		trigger_error('Invalid list of fields passed to clsSelect::addOptions; should be array of two strings.'
		. $this->backtrace(),
		E_USER_ERROR
		);
	}
}


Edit:

Pasted the whole class, not just the method.

I already extend that class with this bit of my own:


    /**
    * extension to Xajax controls library
    * creates html select box pre-selects to a value
    **/

class clsSelectMatched extends clsSelect {

    protected $match;

	function __construct($aConfiguration=array(), $match = 0)
	{
		xajaxControlContainer::xajaxControlContainer('select', $aConfiguration);
        $this->match = $match ;
	}

	function addOption($sValue, $sText )
	{
		$optionNew =& new clsOption();
		$optionNew->setValue($sValue);
		$optionNew->setText($sText);

            if( $sValue == $this->match ) $optionNew->setAttribute('selected', 'selected');

		$this->addChild($optionNew);
	}
}

How ridiculous I am, all I needed was to set PDO::FETCH_ASSOC and pass it to the existing addOptions() and it worked. I had not realised that handles assoc arrays.

Sorry for the waste of space, but its a very strange phenomenon that by writing and sharing a problem you find the solution.

Cheers