Yii2 Query class does not return additional columns - php

I've got something like this
$query = Customer::find()
->select(['customer.name', 'surname', 'cityName' => 'cities.name', 'streetName' => 'streets.name'])
->joinWith(['city', 'street'])
->where(['group_id' => $id]);
When i do
return $query->all();
it returns only columns from customer table, but when i do something like this
$raw = $query->createCommand()->getRawSql();
return \Yii::$app->user_db->createCommand($raw)->queryAll();
it returns me all 4 columns. Why orm fails?
I'm using custom db connection (user), dynamicly connected after authorization. Anyway ActiveRecord->getDb() has been customized too and it works well till now.

it returns only columns from customer table, but when i do something like this.
Yes, that's right. Because Yii2 AR(Active Record) is ORM pattern. And it's try to return all result off query like object.
So, I will not tell the theory, I'd better suggest a solution variant:
$query = Customer::find()
->joinWith(['city', 'street'])
->where(['group_id' => $id])
->asArray()
->all();
return $query;
It's from Yii2 docs(performance tuning).
The result will be like:
[
all data selected from "customer",
['city' => all data selected from city joinWith],
['street' => all data selected from street joinWith]
]
I think, this is exactly what you need.
But, if you need objects. You can try marge objects to only one array.
$customer = Customer::find()
->joinWith(['city', 'street'])
->where(['group_id' => $id])
->all();
return [
'customer' => $customer,
'city' => $customer->city,
'street' => $customer->street,
];

you are using
->select(['customer.name', 'surname', 'cityName' => 'cities.name', 'streetName' => 'streets.name'])
so you will get selected columns only.
use,
$query = Customer::find()
->joinWith(['city', 'street'])
->where(['group_id' => $id])
->all();
this will give you all the columns

Related

How to insert multiple rows in laravel, need explanation

I want to insert multiple rows in a table, where the data collection I am inserting has a unique number. For example : I am inserting 2 row for a user_id number 1. My codes from controller is : I want to keep DB::table() instead of laravel eloquent
foreach($post_data['user_id'] as $key => $no){
$set_base = DB::table('package_user')
->Insert([
'base_id' => $post_data['base_id'],
'base_title' => $post_data['base_title'],
'user_id' => $no,
'package_id' => $post_data['package_id'],
'plan_id' => $post_data['plan_id'],
'currency' => $post_data['currency'],
'payable_plan_amount' => $post_data['total_amount'],
'created_at' => Carbon::now()
]);
}
Please refer How to insert multiple rows from a single query using eloquent/fluent there is a solution for both eloquent and querybuilder
$data = [
['user_id'=>'Coder 1', 'subject_id'=> 4096],
['user_id'=>'Coder 2', 'subject_id'=> 2048],
];
Model::insert($data); // Eloquent approach
DB::table('table')->insert($data); // Query Builder approach
You can also use fill() method if the model instance already created with the pre-defined populated datas.
<code>
$modelObj = new Model();
$modelCollection = collect($request->input())->all();
$modelObj->fill($modelCollection);
$modelObj->save();
</code>

Cake ORM QueryBuilder is selecting data from joined table differently [duplicate]

WHen i try to do :
$fields = array('id' => 'custom_id', 'title' => 'some_name');
The result I get has id as a string.
If I do:
$fields = array('custom_id', 'title' => 'some_name');
then it gives custom_id as integer.
How can I obtain custom_id as id without loosing the data type. I read the documentation but didn't found much help.
There is something that virtual fields can do I think. But is it possible inside the find query without the use of virtual fields etc?
Thanks in Advance
As of CakePHP 3.2
you can use Query::selectTypeMap() to add further types, which are only going to be used for casting the selected fields when data is being retrieved.
$query = $table
->find()
->select(['alias' => 'actual_field', /* ... */]);
$query
->selectTypeMap()
->addDefaults([
'alias' => 'integer'
]);
You can use any of the built-in data types, as well as custom ones. In this case the alias field will now be casted as an integer.
See also
API > \Cake\Database\Query::selectTypeMap()
Cookbook > Database Access & ORM > Database Basics > Data Types
Cookbook > Database Access & ORM > Database Basics > Adding Custom Types
With CakePHP 3.1 and earlier
you'll have to use Query::typeMap(), which will not only affect the selected field when data is being retrieved, but in various other places too where data needs to be casted according to the field types, which might cause unwanted collisions, so use this with care.
$query
->typeMap()
->addDefaults([
'alias' => 'integer'
]);
See also
API > \Cake\Database\Query::typeMap()
Change the type of existing columns
Changing the type of an existing column of a table is possible too, however they need to be set using a specific syntax, ie in the column alias format used by CakePHP, that is, the table alias and the column name seprated by __, eg, for a table with the Articles alias and a column named id, it would be Articles__id.
This can be either set manually, or better yet retrieved via Query::aliasField(), like:
// $field will look like ['Alias__id' => 'Alias.id']
$field = $query->aliasField('id', $table->alias());
$query
->selectTypeMap()
->addDefaults([
key($field) => 'string'
]);
This would change the the default type of the id column to string.
See also
API > \Cake\Datasource\QueryInterface::aliasField()
Hi my alternative example full, user schema() in controller Users add type column aliasFiels by join data:
$this->Users->schema()
->addColumn('is_licensed', [
'type' => 'boolean',
])
->addColumn('total_of_licenses', [
'type' => 'integer',
]);
$fields = [
'Users.id',
'Users.username',
'Users.first_name',
'Users.last_name',
'Users.active',
'Users__is_licensed' => 'if(count(LicenseesUsers.id)>=1,true,false)',
'Users__total_of_licenses' => 'count(LicenseesUsers.id)',
'Users.created',
'Users.modified',
'Languages.id',
'Languages.name',
'Countries.id',
'Countries.name',
'UserRoles.id',
'UserRoles.name',
];
$where = [
'contain' => ['UserRoles', 'Countries', 'Languages'],
'fields' => $fields,
'join' => [
'LicenseesUsers' => [
'table' => 'licensees_users',
'type' => 'LEFT',
'conditions' => [
'Users.id = LicenseesUsers.users_id'
],
],
],
'group' => 'Users.id'
];
// Set pagination
$this->paginate = $where;
// Get data in array
$users = $this->paginate($this->Users)->toArray();

Cakephp-3.x: How to change the data type of a selected alias?

WHen i try to do :
$fields = array('id' => 'custom_id', 'title' => 'some_name');
The result I get has id as a string.
If I do:
$fields = array('custom_id', 'title' => 'some_name');
then it gives custom_id as integer.
How can I obtain custom_id as id without loosing the data type. I read the documentation but didn't found much help.
There is something that virtual fields can do I think. But is it possible inside the find query without the use of virtual fields etc?
Thanks in Advance
As of CakePHP 3.2
you can use Query::selectTypeMap() to add further types, which are only going to be used for casting the selected fields when data is being retrieved.
$query = $table
->find()
->select(['alias' => 'actual_field', /* ... */]);
$query
->selectTypeMap()
->addDefaults([
'alias' => 'integer'
]);
You can use any of the built-in data types, as well as custom ones. In this case the alias field will now be casted as an integer.
See also
API > \Cake\Database\Query::selectTypeMap()
Cookbook > Database Access & ORM > Database Basics > Data Types
Cookbook > Database Access & ORM > Database Basics > Adding Custom Types
With CakePHP 3.1 and earlier
you'll have to use Query::typeMap(), which will not only affect the selected field when data is being retrieved, but in various other places too where data needs to be casted according to the field types, which might cause unwanted collisions, so use this with care.
$query
->typeMap()
->addDefaults([
'alias' => 'integer'
]);
See also
API > \Cake\Database\Query::typeMap()
Change the type of existing columns
Changing the type of an existing column of a table is possible too, however they need to be set using a specific syntax, ie in the column alias format used by CakePHP, that is, the table alias and the column name seprated by __, eg, for a table with the Articles alias and a column named id, it would be Articles__id.
This can be either set manually, or better yet retrieved via Query::aliasField(), like:
// $field will look like ['Alias__id' => 'Alias.id']
$field = $query->aliasField('id', $table->alias());
$query
->selectTypeMap()
->addDefaults([
key($field) => 'string'
]);
This would change the the default type of the id column to string.
See also
API > \Cake\Datasource\QueryInterface::aliasField()
Hi my alternative example full, user schema() in controller Users add type column aliasFiels by join data:
$this->Users->schema()
->addColumn('is_licensed', [
'type' => 'boolean',
])
->addColumn('total_of_licenses', [
'type' => 'integer',
]);
$fields = [
'Users.id',
'Users.username',
'Users.first_name',
'Users.last_name',
'Users.active',
'Users__is_licensed' => 'if(count(LicenseesUsers.id)>=1,true,false)',
'Users__total_of_licenses' => 'count(LicenseesUsers.id)',
'Users.created',
'Users.modified',
'Languages.id',
'Languages.name',
'Countries.id',
'Countries.name',
'UserRoles.id',
'UserRoles.name',
];
$where = [
'contain' => ['UserRoles', 'Countries', 'Languages'],
'fields' => $fields,
'join' => [
'LicenseesUsers' => [
'table' => 'licensees_users',
'type' => 'LEFT',
'conditions' => [
'Users.id = LicenseesUsers.users_id'
],
],
],
'group' => 'Users.id'
];
// Set pagination
$this->paginate = $where;
// Get data in array
$users = $this->paginate($this->Users)->toArray();

Yii2 activerecord select custom attributes from related table

When I'm trying to select custom attributes from record
$data = Emotion::find()
->select('em_id, em_name, tbl_post.post_id, tbl_post.post_header')
->where(['em_id' => $id])
->joinWith('posts')
->one();
I've got an answer model, where related field have full set of table fields.
Trace message looks like this:
app\models\Emotion#1
( [yii\db\BaseActiveRecord:_attributes] =>
[ 'em_id' => 4
'em_name' => 'test_em']
[yii\db\BaseActiveRecord:_related] =>
[ 'posts' =>
[ 0 => app\models\Post#2 ( [yii\db\BaseActiveRecord:_attributes] =>
[ 'post_id' => 10
'post_header' => 'some header'
'post_date' => '2015-06-24 13:40:25',
'post_descr' => 'Rocco, continue the joke'
'post_content' => 'test content'...
So, maybe i did something wrong or this is a framework issue.
Found a workaround here: Yii2 GitHub issue
Which for my case looks like this:
$data = Emotion::find()
->select('em_id, em_name, tbl_post.post_id, tbl_post.post_header')
->where(['em_id' => $id])
->joinWith('posts')
->createCommand()
->queryAll();
And the output of this request will be an raw array.
Is there an another way to have an ActiveRecord object in a response data?
You should simply modify the relation query, e.g. :
$data = Emotion::find()
->select('em_id, em_name')
->where(['em_id' => $id])
->with([
'posts' => function($query) {
$query->select('post_id, post_header');
}
])->one();
And you don't need joinWith since you don't use posts in the main query.

Zf2 HydratingResultSet and Multiple Entities

Just was wondering if ZF2's hydrating resultset can hydrate multiple entities. Consider the snippet below:
$sql = new Sql($this->adapter);
$sqlObject = $sql->select()
->from([
'ART' => 'acl_roles'
])
->join([
'ARTT' => 'acl_role_types',
],
'ART.type_id = ARTT.id',
[
'ARTT.id' => 'id',
'ARTT.identifier' => 'identifier',
'ARTT.name' => 'name',
'ARTT.status' => 'status',
'ARTT.dateAdded' => 'date_added',
],
Select::JOIN_INNER
)
->where([
'ART.identifier' => $identifier,
])
->columns([
'ART.id' => 'id',
'ART.type_id' => 'type_id',
'ART.identifier' => 'identifier',
'ART.name' => 'name',
'ART.status' => 'status',
'ART.description' => 'description',
'ART.dateAdded' => 'date_added',
]);
Now if the query was on a single entity, I could do something like:
$stmt = $sql->prepareStatementForSqlObject($sqlObject);
$resultset = $stmt->execute();
if ($resultset instanceof ResultInterface && $resultset->isQueryResult()) {
$hydratingResultSet = new HydratingResultSet(new ArraySerializable, new EntityClass);
$hydratingResultSet->initialize($resultset);
return $hydratingResultSet->current();
}
However in my case I need the hydrating result set to be able to build and return multiple entities (namely AclRoleEntity and AclRoleTypeEntity). Is this something that is possible? If yes how (considering the result set being a flat array of combination of both entities). If no are there better alternatives to achieve this without using Doctrine/Propel?
Thanks
It's totally possible, you're just going to need a configured (possibly custom) Hydrator.
Your hydrator will need to know the logic to inject your parameters into your objects from a flat array, and how to reduce your object models back to a flat array on extraction.
You're probably looking at a few Hydrator Strategies or a hydrator naming strategy and potentially a combination of both.
With the correct hydrator, you can achieve what you're looking for.

Categories