And if you need to filter attributes in your array you can use this :
public static function convertModelToArray($models, array $filterAttributes = null) {
if (is_array($models))
$arrayMode = TRUE;
else {
$models = array($models);
$arrayMode = FALSE;
}
$result = array();
foreach ($models as $model) {
$attributes = $model->getAttributes();
if (isset($filterAttributes) && is_array($filterAttributes)) {
foreach ($filterAttributes as $key => $value) {
if (strtolower($key) == strtolower($model->tableName()) && strpos($value, '*') === FALSE) {
$value = str_replace(' ', '', $value);
$arrColumn = explode(",", $value);
foreach ($attributes as $key => $value)
if (!in_array($key, $arrColumn))
unset($attributes[$key]);
}
}
}
$relations = array();
foreach ($model->relations() as $key => $related) {
if ($model->hasRelated($key)) {
$relations[$key] = self::convertModelToArray($model->$key, $filterAttributes);
}
}
$all = array_merge($attributes, $relations);
if ($arrayMode)
array_push($result, $all);
else
$result = $all;
}
return $result;
}
and use it like
public function actionComment() {
$db = Comment::model()->with('user','message')->findAll();
echo CJSON::encode(convertModelToArray($db,array('user'=>'id,firstname','message'=>'*')));
}
I fixed this bug and also add a parameter to let you exclude some relations when convert to array.
try this one:
<?php
/**
* Description of JSONUtil
*
* @author
*/
class JSONUtil {
/**
* Converting a Yii model with all relations to a an array.
* @param mixed $models A single model or an array of models for converting to array.
* @param array $filterAttributes should be like array('table name'=>'column names','user'=>'id,firstname,lastname'
* 'comment'=>'*') to filter attributes.
* @param array $ignoreRelations an array contains the model names in relations that will not be converted to array
* @return array array of converted model with all related relations.
*/
public static function convertModelToArray($models, array $filterAttributes = null,array $ignoreRelations=array()) {
if((!is_array($models))&&(is_null($models))) return null;
if (is_array($models))
$arrayMode = TRUE;
else {
$models = array($models);
$arrayMode = FALSE;
}
$result = array();
foreach ($models as $model) {
$attributes = $model->getAttributes();
if (isset($filterAttributes) && is_array($filterAttributes)) {
foreach ($filterAttributes as $key => $value) {
if (strtolower($key) == strtolower($model->tableName())) {
$value = str_replace(' ', '', $value);
$arrColumn = explode(",", $value);
if (strpos($value, '*') === FALSE) {
$attributes = array();
}
foreach ($arrColumn as $column) {
if (($column!='')&&($column != '*')) {
$attributes[$column] = $model->$column;
}
}
//foreach ($attributes as $key => $value) {
//if (!in_array($key, $arrColumn))
//unset($attributes[$key]);
//}
}
}
}
$relations = array();
$key_ignores = array();
if($modelClass = get_class($model)){
if(array_key_exists($modelClass,$ignoreRelations)){
$key_ignores = explode(',',$ignoreRelations[$modelClass]);
}
}
foreach ($model->relations() as $key => $related) {
if ($model->hasRelated($key)) {
if(!in_array($key,$key_ignores))
$relations[$key] = self::convertModelToArray($model->$key, $filterAttributes,$ignoreRelations);
}
}
$all = array_merge($attributes, $relations);
if ($arrayMode)
array_push($result, $all);
else
$result = $all;
}
return $result;
}
}
Great function. I also needed the column names of the output to differ from the actual database ones so I extended the function to support aliases using the $filterAttributes argument. It works just like aliases in SQL. For example if $filterAttributes = array(‘user’ => ‘n AS name’) would result in ‘name’ being the key in the returned array rather than ‘n’.
<?php
/**
* Description of JSONUtil
*
* @link http://www.yiiframework.com/forum/index.php/topic/41922-convert-model-with-relations-to-php-array-and-json/
* @author
*/
class JSONUtil {
/**
* Converting a Yii model with all relations to a an array.
* @param mixed $models A single model or an array of models for converting to array.
* @param array $filterAttributes should be like array('table name'=>'column names','user'=>'id,firstname,lastname'
* 'comment'=>'*') to filter attributes. Also can use alias for column names by using AS with the column name just
* like in SQL.
* @param array $ignoreRelations an array contains the model names in relations that will not be converted to array
* @return array array of converted model with all related relations.
*/
public static function convertModelToArray($models, array $filterAttributes = null,array $ignoreRelations=array())
{
if((!is_array($models))&&(is_null($models))) return null;
if (is_array($models))
$arrayMode = TRUE;
else {
$models = array($models);
$arrayMode = FALSE;
}
$result = array();
foreach ($models as $model) {
$attributes = $model->getAttributes();
if (isset($filterAttributes) && is_array($filterAttributes)) {
foreach ($filterAttributes as $key => $value) {
if (strtolower($key) == strtolower($model->tableName())) {
$arrColumn = explode(",", $value);
if (strpos($value, '*') === FALSE) {
$attributes = array();
}
foreach ($arrColumn as $column)
{
$columnNameAlias = array_map('trim', preg_split("/[aA][sS]/", $column));
$columnName = '';
$columnAlias = '';
if(count($columnNameAlias) === 2)
{
$columnName = $columnNameAlias[0];
$columnAlias = $columnNameAlias[1];
}
else
{
$columnName = $columnNameAlias[0];
}
if(($columnName != '') && ($column != '*'))
{
if($columnAlias !== '')
{
$attributes[$columnAlias] = $model->$columnName;
}
else
{
$attributes[$columnName] = $model->$columnName;
}
}
}
}
}
}
$relations = array();
$key_ignores = array();
if($modelClass = get_class($model)){
if(array_key_exists($modelClass,$ignoreRelations)){
$key_ignores = explode(',',$ignoreRelations[$modelClass]);
}
}
foreach ($model->relations() as $key => $related) {
if ($model->hasRelated($key)) {
if(!in_array($key,$key_ignores))
$relations[$key] = self::convertModelToArray($model->$key, $filterAttributes,$ignoreRelations);
}
}
$all = array_merge($attributes, $relations);
if ($arrayMode)
array_push($result, $all);
else
$result = $all;
}
return $result;
}
}
It’s an ugly one, but rather than complicating it and setting up different virtual attributes, include them in a fuzzy way in the result of ->getAttributes() I chose to amend the code with a small if checking specifically for the STAT relation:
public function convertModelToArray($models) {
if (is_array($models))
$arrayMode = TRUE;
else {
$models = array($models);
$arrayMode = FALSE;
}
$result = array();
foreach ($models as $model) {
$attributes = $model->getAttributes();
$relations = array();
foreach ($model->relations() as $key => $related) {
if ($model->hasRelated($key)) {
if ($related[0] == "CStatRelation")
$relations[$key] = $model->$key;
else
$relations[$key] = $this->convertModelToArray($model->$key);
}
}
$all = array_merge(array_filter($attributes,'count'), array_filter($relations,'count'));
if ($arrayMode)
array_push($result, $all);
else
$result = $all;
}
return $result;
}
Btw I also use array_filter with the callback of ‘count’ to check not include the fields that are “” or NULL but leave and include the ‘0’ (zero int).