Multiple count in ORM is not working - cakephp

I want to get count of tenancies based on differnet times
$start_date = $end_date = $end_date1 = new \DateTime('first day of last month');
$start_date = $start_date->format('Y-m-01 00:00:00');
$end_date = $end_date->format('Y-m-t 23:59:59');
$end_date1 = $end_date1->format('Y-m-'. date('d') .' 23:59:59');
$start_date2 = $end_date2 = new \DateTime('now');
$start_date2 = $start_date2->format('Y-m-01 00:00:00');
$end_date2 = $end_date2->format('Y-m-d 23:59:59');
$tenancyDetails = $this
->find()
->select([
'total_last_month' => $this->find()->func()->count('Tenancy.id'),
'count_last_month' => $this->find()->func()->count('Tenancy1.id'),
'count_curr_month' => $this->find()->func()->count('Tenancy2.id'),
'Tenancy.created', 'Tenancy1.created', 'Tenancy2.created',
])
->leftJoin(['Tenancy1' => 'tenancy'],[
'Tenancy1.active' => 1,
'Tenancy1.stage !=' => 1,
'Tenancy1.company_id' => $id,
])
->leftJoin(['Tenancy2' => 'tenancy'],[
'Tenancy2.active' => 1,
'Tenancy2.stage !=' => 1,
'Tenancy2.company_id' => $id,
])
->where([
'Tenancy.active' => 1,
'Tenancy.stage !=' => 1,
'Tenancy.company_id' => $id,
])
->where(function ($exp, $q) use ($start_date, $end_date) {
return $exp->between('Tenancy.created', $start_date, $end_date);
})
->where(function ($exp, $q) use ($start_date, $end_date1) {
return $exp->between('Tenancy1.created', $start_date, $end_date1);
})
->where(function ($exp, $q) use ($start_date2, $end_date2) {
return $exp->between('Tenancy2.created', $start_date2, $end_date2);
})->toArray();
Here is what I get. The number of counts are the same. Eventhough the datesare different
[
(int) 0 => object(App\Model\Entity\Tenancy) {
'total_last_month' => (int) 4590,
'count_last_month' => (int) 4590,
'count_curr_month' => (int) 4590,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2016-03-01T10:57:21+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'Tenancy1' => [
'created' => '2016-03-01 10:57:21'
],
'Tenancy2' => [
'created' => '2016-04-04 10:59:42'
],
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tenancy'
}
]
Even though I have written the query this way but still the same problem.
$query = $tenancies->find();
$query->select([
'total_last_month' => $query->func()->count('Tenancy.id'),
])
->where([
'Tenancy.active' => 1,
'Tenancy.stage !=' => 1,
'Tenancy.company_id' => $id,
])
->where(function ($exp) use ($start_date, $end_date) {
return $exp->between('Tenancy.created', $start_date, $end_date);
});
$query->select([
'count_last_month' => $query->func()->count('Tenancy1.id'),
])
->leftJoin(['Tenancy1' => 'tenancy'],[
'Tenancy1.company_id' => $id,
])
->where([
'Tenancy1.active' => 1,
'Tenancy1.stage !=' => 1,
])
->where(function ($exp) use ($start_date, $end_date1) {
return $exp->between('Tenancy1.created', $start_date, $end_date1);
});
I can see the printed query which is correct.

After looking at all the examples in the documentation it looks like you need to instantiate the ORM query builder instance before using the func() helpers.
http://book.cakephp.org/3.0/en/orm/query-builder.html#using-sql-functions
In theory $this->func() should work the same if the return is simply a string. but maybe using $this->func() results in a disconnect between the query instance you are trying to build and an unbound new instance?
try:
$tenancyDetails = $this>find();
$tenancyDetails->select([
'total_last_month' => $tenancyDetails->find()->func()->count('Tenancy.id'),
'count_last_month' => $tenancyDetails->find()->func()->count('Tenancy1.id'),
'count_curr_month' => $tenancyDetails->find()->func()->count('Tenancy2.id'),
'Tenancy.created',
'Tenancy1.created',
'Tenancy2.created',
])......
Edit
I checked the code for the count function in /ORM/query.php file.
If no values are set cake will perform the count() query on the call $this->func->count(). The PHP compiler must be reading and therefore executing the count query before the query has been built, meaning before your conditions have been set. That's why you you need to instantiate the class first to stop the count query executing prematurely.

Related

cant stop null assciated models from beign returned in cakephp 3

I am trying to retrieve records from a primary and associated model where the associated model has a condition on it. The is a basic one to many relationship. The WpPosts is the primary and i want all associated rows from WpPostmetas that have the meta_value of lead_data from a data range. The below code does this but it also including null associated models to the WpPost output. The below data should not have been retrieved. How do i prevent the below data from being retrieved?
object(App\Model\Entity\WpPost) {
'ID' => (int) 1997,
'post_author' => (int) 1,
'post_date' => object(Cake\I18n\FrozenTime) {
'time' => '2018-09-27T12:08:40+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'post_date_gmt' => object(Cake\I18n\FrozenTime) {
'time' => '2018-09-27T12:08:40+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
.....
'comment_count' => (int) 0,
'wp_postmetas' => [], //see null value should be included
///query works but also included null associated models
$q2= $this->WpPosts->find()->contain('WpPostmetas', function ($q) {
return $q
->where(['meta_key LIKE' => '%lead_data%' ]);
})
->where(['WpPosts.post_date >=' => $searchDate ])
->order(['WpPosts.id' => 'DESC'])
->enableHydration(true);

cant save mulitple records in cakephp3

I cant save multiple records in cakephp3 v3.2. I have a field to reset to 0 on about 20 rows of data (see below). The below code doesnt save. I dont get an error and a debug of the data is correct but nothing happens, no errors. Just it doesnt reset that field to 0.
I can get it to work using execute method as below but why doesnt newentites work?
private function tmpschedules( $tmpscheduleStudents=0, $tutorId=0){
foreach ( $tmpscheduleStudents as $key => $item) :
// debug($item);
$tmpscheduleStudents[$key]['allocated']=0;
endforeach;
// exit;
$students_schedule = $this->Tmpschedules->newEntities($tmpscheduleStudents, ['validate' => false]);
foreach ( $students_schedule as $key => $item) {
debug($item);
$result=$this->Tmpschedules->save($item, ['atomic' => false]);
}
exit;
return 1;
}
//see allocated field is 0 before being saved but it doesnt save.
object(App\Model\Entity\Tmpschedule) {
'student_id' => (int) 1039,
'tutor_id' => (int) 92,
'rank' => (int) 0,
'inactive' => (int) 0,
'weekday_start' => (int) 0,
'start_order' => (int) 0,
'allocated' => (int) 0,
//this does work
foreach ( $tmpscheduleStudents as $key => $item) :
// debug($item);
// $tmpscheduleStudents[$key]['allocated']=0;
$query = $this->Tmpschedules->query();
$query->update()
->set(['allocated' => 0])
->where(['tutor_id' => $tutorId])
->execute();
endforeach;
//result update which has allocated as o0 but doesnt save and no errors as you see below
'student_id' => (int) 245,
'tutor_id' => (int) 13,
'rank' => (int) 0,
'inactive' => (int) 0,
'weekday_start' => (int) 0,
'start_order' => (int) 0,
'allocated' => (int) 0,
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'student_id' => true,
'tutor_id' => true,
'rank' => true,
'inactive' => true,
'weekday_start' => true,
'start_order' => true,
'allocated' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tmpschedule2s'
}
What is the field type? If the field type and the value you are inserting do not match it will be getting marshaled away. My guess is that is what is happening. The key difference is in one of your examples you are casting and in the other you are not casting also in some you use 0 vs false. Keep consistent.
The following should work
$students_schedule = $this->Tmpschedules->newEntities($tmpscheduleStudents, ['validate' => 0]);
You could also get away with
$students_schedule = $this->Tmpschedules->newEntities($tmpscheduleStudents, ['validate' => (int)false]);
just make sure that your column type and value type match and that should resolve your issue. My example assumes you are using a numeric type field.

How to union when using matching

I am trying to list public status posts and friends posts
getting friends post
$friendsPosts= $this->Posts->find('all')
->contain(['Users', 'Languages', 'PostStates'])
->matching('Users.Dusers', function ($q) {
return $q->where(['Dusers.id' => $this->Auth->user('id')]);
});
getting public post
$posts= $this->Posts->find('all')
->where(['Posts.post_state_id' => 3])
->contain(['Users', 'Languages', 'PostStates']);
$posts->union($friendsPosts);
dd($posts->toArray());
error message: The used SELECT statements have a different number of columns
Here is solution
$posts = $this->Posts->find()
->where(['Posts.post_state_id' => 3])
->contain(['Users', 'PostStates', 'Languages', 'Tags', 'Translations' => ['Users', 'Languages', 'conditions' => ['post_state_id' => 3]]]);
$friendsPosts = $this->Posts->find('all')
->where(['Posts.post_state_id' => 2])
->contain(['PostStates', 'Languages', 'Users', 'Tags', 'Translations' => ['Users', 'Languages', 'conditions' => ['post_state_id' => 3]]])
->innerJoinWith('Users.Dusers', function ($q) {
return $q->where(['Dusers.id' => $this->Auth->user('id'), 'UsersUsers.status' => 1])->select($this->Posts->Users);
});
$posts->union($friendsPosts);

Mock auth cakephp 3

Currently I'm trying to test a controller which uses the authentication component to retrieve the user id. Since I'm fairly new to unit/integration testing, I have no idea how to make this working. Moreover, all the content that I could find for this particular problem is written for Cakephp version 2.
Controller function:
public function favorite(){
// Particular line where the problem is occurring:
$userId = $this->Auth->User()['id'];
// Get all favorite pictures of the user
$query = $this->UsersPictures->getFavoritePictures($userId);
$results = $query->all();
// Replace the link in the result set by a presigned url of Amazon
foreach($results as $result){
$result->picture->link = $this->Aws->getTokenizedItem($result->picture->link);
}
$this->set([
'success' => true,
'data' => [
'pictures' => $results
],
'_serialize' => ['success', 'data']
]);
}
Integration test:
public function testFavoriteShouldPass(){
$this->configRequest([
'headers' => [
'Accept' => 'application/json'
]
]);
$this->get('api/pictures/favorite/1.json');
$expected = [
'success' => true,
'data' => [
[
'user_id' => 1,
'picture_id' => 1,
'created' => '2016-04-03T20:35:40+0000',
'modified' => '2016-04-03T20:35:40+0000',
'picture' => [
'id' => 1,
'album_id' => 1,
'description' => 'Lorem ipsum dolor sit amet',
'link' => 'test',
'favorite' => true,
'created' => null,
'modified' => null,
'cover_photo' => true
]
]
]
];
$this->assertEquals($expected, $response);
}
My question is how can I insert a user with a default id of 1 for $this->Auth->User()['id']. I saw in other questions that I need to use something that looks like this:
$this->_controller->Auth
->staticExpects($this->any())
->method('user')
->will($this->returnValue([
'id' => 1,
'username' => 'admin',
'created' => '2013-05-08 00:00:00',
'modified' => '2013-05-08 00:00:00',
'email' => 'me#me.com',
]));
However, I read that staticExpects is deprecated from phpunit version 3.8 (I'm using 5.2). How should I mock this?
You can set the session data in your integration tests using $this->session(), for example (taken from cakephp book):
// Set session data
$this->session([
'Auth' => [
'User' => [
'id' => 1,
'username' => 'testing',
// other keys.
]
]
]);
You can actually find it in their docs: http://book.cakephp.org/3.0/en/development/testing.html#controller-integration-testing
You can find it in the section
Testing Actions That Require Authentication
If you want to use the same session data on each test you can use the setUp method of your integration test class, like so:
public function setUp()
{
parent::setUp();
// Set session data
$this->session([
'Auth' => [
'User' => [
'id' => 1,
'username' => 'testing',
// other keys.
]
]
]);
}
With thanks to azahar :), this worked for me:
public function controllerSpy($event)
{
parent::controllerSpy($event);
if (isset($this->_controller)) {
$this->_controller->Auth->setUser([
'id' => 1,
'username' => 'testtesttesttest',
'email' => 'john#doe.com',
'first_name' => 'John',
'last_name' => 'Doe',
'uuid' => 'wepoewoweo-ew-ewewpoeopw',
'sign_in_count' => 1,
'current_sign_in_ip' => '127.0.0.1',
'active' => true
]);
}
}

How to get only selected fields from contain in cakephp3

I have a query as below but I need only certain fields not all the fields. I have set the autoFields to false but the query still returns all the fields.
$tenancies = $this
->find('all')
->autoFields(false)
->select([
'Tenancy.id', 'Tenancy.created', 'Tenancy.stage',
'Properties.id', 'Properties.address1', 'Properties.postcode',
'Tenants.stage',
])
->contain('Properties', function(\Cake\ORM\Query $query) {
return $query->where([
'Properties.active' => 1
]);
})
->contain(['Tenants'])
->leftJoinWith('Tenants', function(\Cake\ORM\Query $query) {
return $query
->autoFields(false)
->select([
'id', 'stage'
])
->where([
'Tenants.active' => 1
]);
})
->where([
'Tenancy.active' => 1,
$conditions
])
->order([
'Tenancy.created' => 'DESC',
'Tenants.tenancy_id'
])
->group(['Properties.id'])
->autoFields(false);
return $tenancies;
Prints =>
object(App\Model\Entity\Tenancy) {
'id' => (int) 3934,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2016-03-20T18:25:20+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'stage' => (int) 2,
'tenants' => [
(int) 0 => object(Cake\ORM\Entity) {
'id' => (int) 8922,
'user_id' => (int) 56456,
'tenancy_id' => (int) 3934,
'needs_guarantor' => true,
'guarantor_id' => null,
'holding_fee' => (float) 0,
...
...
Note: Using matching or leftJoinWith I can select the fields that I need but it doesn't work on contain
The main answer is you need to set the associations correctly. Once the associations are set correctly you can print it as simple as this.
return $this->find()
->select([
'Property.id', 'Property.company_id', 'Property.address1', 'Property.address2',
'Property.address3','Property.postcode',
])
->contain([
'Tenancies' => function($q) {
return $q
->select([
'Tenancies.id','Tenancies.property_id','Tenancies.created',
'Tenancies.stage',
])
->contain([
'Tenants' => function($q) {
return $q
->select([
'Tenants.id', 'Tenants.stage', 'Tenants.tenancy_id', 'Tenants.holding_fee',
])
->where([
'active = 1',
]);
}
])
->contain([
'Tenants'
])
->where([
'Tenancies.active = 1',
]);
}
])
->where(['Property.active = 1', $conditions])
->toArray();

Resources