RESOLVED cakephp 4 - Test mail in command - cakephp

How can I test that a mail sent in a command is correct ?
I have a testcase for the command, but I cannot use : https://book.cakephp.org/4/en/core-libraries/email.html#testing-mailers.
I use cake 4.3.4
The test case is:
use EmailTrait;
/**
* Test execute method
*
* #return void
* #uses \App\Command\NextweekCommand::execute()
*/
public function testExecute(): void
{
$this->exec('nextweek');
$this->assertMailSentTo('dype#localhost');
}
And in the nextweek command, the mail is sent by:
$email = new Mailer();
$email->setViewVars([
'saison' => date("Y", strtotime('next Saturday')),
'titre' => $titre,
'evenements' => $evenements,
'resultats' => $resultats,
'domaine' => $this->domaine,
'ecoles' => $ecoles,
'statSite' => $statSite
]);
$email
->setFrom([$this->myconf['listediffasso'] => 'ASSO'])
->setReplyTo([$this->myconf['listediffasso'] => 'ASSO'])
->setTo($this->myconf['listediffasso'])
->setSubject('Next week ')
->setEmailFormat('html')
->viewBuilder()
->setTemplate('nextweek')
->setLayout('asso1');
$dest = $this->Users->getUserMailSynthese();
foreach ($dest as $d) {
$email->addCc($d->email);
}
$email->deliver();
Thanks

Related

Cakephp 3 PHPUnit ingration test fails after the test before

I have the UsersFixture with three records.
The test methods first() and second(), which both are before guest_can_login(), pr's show "Joe", "Joe", as expected. But with test method third(), which comes after guest_can_login(), I get notice error: trying to get property of non-object.
So, for a reason, something in the guest_can_login() breaks the rest of the test methods. I have tried by making a duplicate of guest_can_login() as well.
I think it is strange, as tearDown should "reset" everything after each test. I'm out of ideas. And after reading the Cakephp Testing docs, I haven't been able to solve it.
Any suggestions to help me solve this is much appreciated.
Code below (gist if you prefer: https://gist.github.com/chris-andre/2eb3ad053073caf4f1c81722428a900b):
public $fixtures = [
'app.users',
'app.tenants',
'app.roles',
'app.roles_users',
];
public $Users;
public function setUp()
{
parent::setUp();
$config = TableRegistry::getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
$this->Users = TableRegistry::getTableLocator()->get('Users', $config);
}
/**
* tearDown method
*
* #return void
*/
public function tearDown()
{
unset($this->Users);
TableRegistry::clear();
parent::tearDown();
}
/** #test */
public function guest_can_register()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true
];
$this->post('/register', $data);
$this->assertResponseSuccess();
$this->assertRedirect(['controller' => 'Users', 'action' => 'login', '_host' => 'testcomp.timbas.test']);
$user = $this->Users->find()
->contain(['Tenants', 'Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first();
}
/** #test */
public function first()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function second()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function guest_can_login()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'testcomp.timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true,
'roles' => ['_ids' => [ADMINISTRATOR_ROLE_ID]]
];
$user = $this->Users->newEntity($data, [
'associated' => ['Tenants', 'Roles']
]);
$this->Users->save($user);
$getNewUser = $this->Users->find()
->contain(['Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first()
->toArray();
// pr($getNewUser->id);
$this->post('/users/login', [
'email' => 'chris#andre.com',
'password' => '123456'
]);
$this->assertSession($getNewUser, 'Auth.User');
}
/** #test */
public function third()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
EDIT 2018-08-06:
Users::register() is a global context, I cannot be accessed from url with subdomain. E.g. tenant1.domain.com/register will throw a badRequest, while domain.com/register is a valid url. On registration success, user is forwarded to login from right url. Login-url = Tenants.domain + domain + suffix, e.g. tenant1.domain.com. When user is on the tenant scope (url with subdomain), the Tenants.id where Tenants.domain = tenant1, will be added to the where clause in all queries for the models having the behavior attached.
Now, what happens in third() is that the tenant_id from the newly created Tenant in guest_can_login() is added to the query, which means the test "is still on the tenant scope" when third() is run. That is the problem.
The other problem is that setUp() is called on all test methods but third(). testDown() is called on every test methods.
App\Middleware\TenantMiddleware.php:
use InstanceConfigTrait;
/**
* Default config.
* Options:
* - globalScope: tells the middleware what controller and action tenant scope is not being used
* Example
* 'globalScope' => [
* 'Pages' => ['*'], // All actions in PagesController is global
* 'Users' => ['register'] // Register action in UsersController is global
* ]
* #var array
*/
protected $_defaultConfig = [
'globalScope' => [
'Users' => ['register'],
'Landing' => ['*'],
'Pages' => ['*']
],
];
public function __construct($config = [])
{
if (!isset($config['primaryDomain'])) {
$config['primaryDomain'] = Configure::read('Site.domain');
}
$this->setConfig($config);
}
/**
* Invoke method.
*
* #param \Cake\Http\ServerRequest $request The request.
* #param \Psr\Http\Message\ResponseInterface $response The response.
* #param callable $next Callback to invoke the next middleware.
* #return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
// Get subdomains
$subdomains = $request->subdomains();
// If subdomains not empty, the first is always the tenants domain
$subdomain = !empty($subdomains) ? $subdomains[0] : '';
Tenant::setDomain($subdomain);
// Get params of current request
$params = $request->getAttribute('params');
$controller = $params['controller'];
$action = $params['action'];
// Set tenantScope as default
Tenant::setScope('tenant');
$globalScope = $this->getConfig('globalScope');
// If Controller and action is a global scope
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) || in_array('*', $globalScope[$controller])) {
Tenant::setScope('global');
}
}
if (
(Tenant::getScope() === 'tenant' && Tenant::tenant() === null)
|| (Tenant::getDomain() === '' && Tenant::getScope() === 'tenant')
|| (Tenant::getDomain() !== '' && Tenant::getScope() === 'global')
) {
throw new NotFoundException('The page you are looking for does not exists.');
}
$primaryDomain = $this->getConfig('primaryDomain');
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) && Tenant::getScope() === 'global') {
}
}
return $next($request, $response);
}
App\Model\Behavior\TenantScopeBehavior.php:
protected $_table;
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
public function __construct(Table $table, array $config = [])
{
parent::__construct($table, $config);
}
public function beforeFind(Event $event, Query $query, ArrayObject $options)
{
$model = $this->_table->getAlias();
$foreig_key = 'tenant_id';
if (!isset($options['skipTenantCheck']) || $options['skipTenantCheck'] !== true) {
if (Tenant::getScope() === 'tenant') {
if ($model === 'Tenants') {
$query->where(['Tenants.id' => Tenant::tenant()->id]);
} else {
$query->where([$model . '.' . $foreig_key => Tenant::tenant()->id]);
}
}
}
return $query;
}
public function beforeSave(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->isNew()) {
$entity->tenant_id = Tenant::tenant()->id;
} else {
// Check if current tenant is owner
if ($this->_table->getAlias() === 'Tenants') {
if ($entity->id != Tenant::tenant()->id) {
throw new BadRequestException();
}
} else {
if ($entity->tenant_id != Tenant::tenant()->id) {
throw new BadRequestException();
}
}
}
}
return true;
}
public function beforeDelete(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->tenant_id != Tenant::tenant()->id) { //current tenant is NOT owner
throw new BadRequestException();
}
}
return true;
}
App\Tenant\Tenant.php:
/**
* $_domain will be empty or comtain the domain (subdomain from url)
* #var string can be empty
*/
protected static $_domain;
/**
* $_scope shall be 'global' or 'tenant'.
* #var string
*/
protected static $_scope;
/**
* #var null|object \App\Model\Entity\Tenant
*/
protected static $_tenant;
/**
* Gets domain from $_domain and returns the string
* #return string
*/
public static function getDomain()
{
return self::$_domain;
}
/**
* Set the tenant scope domain. Will be set in the TenantMiddleware, and shall not be set anywhere else
* #param string $domain
* #return string empty or with domain
*/
public static function setDomain($domain)
{
self::$_domain = $domain;
}
/**
* Tenant method
* Return the object \App\Model\Table\Tenants ro null
* #return type
*/
public static function tenant()
{
$tenant = static::_getTenant();
return $tenant;
}
protected static function _getTenant()
{
if (self::$_tenant === null) {
$cachedTenants = Cache::read('tenants');
if($cachedTenants !== false) {
// do something
}
$tenantsTable = TableRegistry::get('Tenants');
$tenant = $tenantsTable->find('all', ['skipTenantCheck' => true])
->where(['Tenants.domain' => self::getDomain()])
->where(['Tenants.active' => true])
->first();
self::$_tenant = $tenant;
}
return self::$_tenant;
}
public static function getScope()
{
return self::$_scope;
}
/**
* Description
* #param type $scope
* #return type
*/
public static function setScope($scope)
{
self::$_scope = $scope;
}

Mock databases function from drupal 7

I want to create a phpunit test from the following code, but I'm not even sure if it worth it, because the code contains db_update and db_query functions, so, those are database functions and I don't know if it make sense create a test for this, because I don't know if should assume drupal code is working fine.
<?php
namespace Drupal\forum_innovation\Forum;
/**
* Created by PhpStorm.
* User: ldcontreras
* Date: 30/05/18
* Time: 18:26
*/
class ForumCounter implements ForumInterface {
public static function setForumCounter($forum, $uid) {
$counterState = db_update('forum_counter_states')
->fields(array(
'state' => 'read',
))
->condition('uid', $uid)
->condition('tid', $forum)
->execute();
return $counterState;
}
public static function getForumNotification($forum, $uid) {
$unReadNotifications =
db_query('SELECT count(*) as counter
FROM {forum_counter_states} as f WHERE f.uid = :uid AND f.state = :state AND f.tid = :forum',
array(
':uid' => $uid,
':forum' => $forum,
':state' => 'unread'
)
)->fetchAll();
return $unReadNotifications[0]->counter;
}
}
I'm trying to create a test like this, but I need some help and clarification:
<?php
/**
* Created by PhpStorm.
* User: ldcontreras
* Date: 8/06/18
* Time: 10:02
*/
namespace Drupal\forum_innovation\Forum;
class ForumCounterTest extends \PHPUnit_Framework_TestCase {
public function TestSetForumCounter() {
$db_query = $this->getMock('db_update', array('fields', 'condition', 'execute'));
$db_query->expects($this->once())->method('fields')->with($this->equalTo(array(':uid' => 3024));
$db_query->expects($this->once())->method('condition')->with($this->equalTo(array(':uid' => 3024)));
$db_query->expects($this->once())->method('condition')->with($this->equalTo(
array(':tid' => 83))->will(
$this->returnCallback('callback'));
}
}
Thanks!

Fatal error: Declaration of CRMCoreContactController::save($contact) must be compatible with EntityAPIController::save

I recently installed CRM Core and all of its missing modules needed to run it. Sadly I need this module for the project that I am working on but the second I installed them I got this error.
Fatal error: Declaration of CRMCoreContactController::save($contact) must be compatible with EntityAPIController::save($entity, ?DatabaseTransaction $transaction = NULL) in /opt/lampp/htdocs/drupal/modules/crm_core/modules/crm_core_contact/includes/crm_core_contact.controller.inc on line 111
I went back in the code and I couldn't see what to change. Line 111 is the ver last line of the code. Ill paste the code as well maybe someone out there knows how to solve this, please.
<?php
/**
* CRM Contact Entity Class.
*/
class CRMCoreContactEntity extends Entity {
protected function defaultLabel() {
return crm_core_contact_label($this);
}
protected function defaultUri() {
return array(
'path' => 'crm-core/contact/' . $this->identifier(),
'options' => array(
'absolute' => TRUE,
),
);
}
/**
* Method for de-duplicating contacts.
*
* Allows various modules to identify duplicate contact records through
* hook_crm_core_contact_match. This function should implement it's
* own contact matching scheme.
*
* #return array
* Array of matched contact IDs.
*/
public function match() {
$checks = & drupal_static(__FUNCTION__);
$matches = array();
if (!isset($checks->processed)) {
$checks = new stdClass();
$checks->engines = module_implements('crm_core_contact_match');
$checks->processed = 1;
}
// Pass in the contact and the matches array as references.
// This will allow various matching tools to modify the contact
// and the list of matches.
$values = array(
'contact' => &$this,
'matches' => &$matches,
);
foreach ($checks->engines as $module) {
module_invoke($module, 'crm_core_contact_match', $values);
}
// It's up to implementing modules to handle the matching logic.
// Most often, the match to be used should be the one
// at the top of the stack.
return $matches;
}
}
/**
* #file
* Controller class for contacts.
*
* This extends the DrupalDefaultEntityController class, adding required
* special handling for contact objects.
*/
class CRMCoreContactController extends EntityAPIController {
public $revisionKey = 'vid';
public $revisionTable = 'crm_core_contact_revision';
/**
* Create a basic contact object.
*/
public function create(array $values = array()) {
global $user;
$values += array(
'contact_id' => '',
'vid' => '',
'uid' => $user->uid,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
return parent::create($values);
}
/**
* Update contact object before saving revision.
*/
protected function saveRevision($entity) {
if (!isset($entity->log)) {
$entity->log = '';
}
$entity->is_new_revision = TRUE;
$entity->uid = $GLOBALS['user']->uid;
return parent::saveRevision($entity);
}
/**
* Updates 'changed' property on save.
*/
public function save($contact) {
$contact->changed = REQUEST_TIME;
// Storing formatted contact label for autocomplete lookups.
$contact->name = crm_core_contact_label($contact);
return parent::save($contact);
}
}
Changing
public function save($contact)
to
public function save($contact, DatabaseTransaction $transaction = NULL)
should work.
You need to switch from PHP 7.x+ to PHP 5.6. This will resolve this error.
Can't give you more advice on how to downgrade without more details on what system you're running but there are many guides out there on this topic.

How to save in database the data response by paypal in laravel?

I am trying to save the data in database which is response by paypal.
Here is my try, I get the data from paypal response like this.
public function getDone(Request $request)
{
$id = $request->get('paymentId');
$token = $request->get('token');
$payer_id = $request->get('PayerID');
$payment = PayPal::getById($id, $this->_apiContext);
$paymentExecution = PayPal::PaymentExecution();
$paymentExecution->setPayerId($payer_id);
$executePayment = $payment->execute($paymentExecution, $this->_apiContext);
print_r($executePayment);
}
the result is here
PayPal\Rest\ApiContext Object ( [requestId:PayPal\Rest\ApiContext:private] => 216056242044171493312944593 [credential:PayPal\Rest\ApiContext:private] => PayPal\Auth\OAuthTokenCredential Object ( [logger:PayPal\Auth\OAuthTokenCredential:private] => PayPal\Core\PayPalLoggingManager Object ( [loggerName:PayPal\Core\PayPalLoggingManager:private] => PayPal\Auth\OAuthTokenCredential [isLoggingEnabled:PayPal\Core\PayPalLoggingManager:private] => [loggingLevel:PayPal\Core\PayPalLoggingManager:private] => [loggerFile:PayPal\Core\PayPalLoggingManager:private] => ) [clientId:PayPal\Auth\OAuthTokenCredential:private] => AVFGCgHNihKu_MpwgbpDPuB9FO8Z5hdgGrEx1mZPhQoOs2vLzFf1Dv6qA2bN5Ja_qucPYkN0X5Hrz9ZE [clientSecret:PayPal\Auth\OAuthTokenCredential:private] => EBdP51X4tMAi9WYcgI1ue0TQLljyvDBxO1yQAFJn7X7z_GP7mAOMnAsDCsREotfEOCESD_D6Qp14plTy [accessToken:PayPal\Auth\OAuthTokenCredential:private] => A21AAG85CiMoJ6JFJfQIfSMdzPm1sbwy7AZcT1zyxNbfw2-WXms8hgALJK3uFJy9DfG3oVQTKrIEApTwvDoiXAp49SrIiBfmw [tokenExpiresIn:PayPal\Auth\OAuthTokenCredential:private] => 32315 [tokenCreateTime:PayPal\Auth\OAuthTokenCredential:private] => 1493312949 [cipher:PayPal\Auth\OAuthTokenCredential:private] => PayPal\Security\Cipher Object ( [secretKey:PayPal\Security\Cipher:private] => EBdP51X4tMAi9WYcgI1ue0TQLljyvDBxO1yQAFJn7X7z_GP7mAOMnAsDCsREotfEOCESD_D6Qp14plTy ) [_propMap:PayPal\Common\PayPalModel:private] => Array ( ) ) )
I want to save in database and how i try to do it is here
public function getDone(Request $request)
{
$id = $request->get('paymentId');
$token = $request->get('token');
$payer_id = $request->get('PayerID');
$payment = PayPal::getById($id, $this->_apiContext);
$paymentExecution = PayPal::PaymentExecution();
$paymentExecution->setPayerId($payer_id);
$executePayment = $payment->execute($paymentExecution, $this->_apiContext);
if($request->tx){
if($payment=Payment::where('transaction_id',$request->tx)->first()){
$payment_id=$payment->id;
}else{
$payment=new Payment;
$payment->transaction_id=$request->email;
$payment->transaction_id=$request->name;
$payment->item_number=$request->item_number;
$payment->transaction_id=$request->tx;
$payment->currency_code=$request->cc;
$payment->payment_status=$request->st;
$payment->save();
$payment_id=$payment->id;
}
return 'Pyament has been done and your payment id is : '.$payment_id;
}else{
return 'Payment has failed';
}
}
The error is showing like
Payment has failed
Here is my database which is trying to save in
public function up()
{
Schema::create('payments', function (Blueprint $table) {
$table->increments('id');
$table->string('email');
$table->string('name');
$table->string('item_number');
$table->string('transaction_id');
$table->string('currency_code');
$table->string('payment_status');
$table->timestamps();
});
}
Where am i wrong in my codes, please guide me to save in database.
Please guide me. Thanks.

cakephp save data is giving me an error

I am trying to insert the following array
foreach($list as $l)
{
$data = array(
'source_number'=> $l['Csv']['Source'],
'destination_number'=> $l['Csv']['Destination'],
'seconds'=> $l['Csv']['Seconds'],
'callerID'=> $l['Csv']['CallerID'],
'disposition'=> $l['Csv']['Disposition'],
'cost'=> $l['Csv']['Cost'],
'billing_cost'=> $l['Csv']['newCost']
);
//$total[] = $l['Csv']['newCost'];
debug($data);
$this->CallcenterBilling->save($data);
unset($data);
}
But it dives me this error
( ! ) Fatal error: Call to a member function save() on a non-object in C:\wamp\www\wadic\app\Controller\CallcenterBillingsController.php on line 61
Call Stack
# Time Memory Function Location
1 0.0005 708160 {main}( ) ..\index.php:0
2 0.0169 2775744 Dispatcher->dispatch( ) ..\index.php:96
3 0.0279 3946176 Dispatcher->_invoke( ) ..\Dispatcher.php:89
4 0.0293 4016984 Controller->invokeAction( ) ..\Dispatcher.php:107
5 0.0293 4017992 ReflectionMethod->invokeArgs( ) ..\Controller.php:473
6 0.0293 4018024 CallcenterBillingsController->import( ) ..\CallcenterBillingsController.php:0
what am I missing?
thanks
Firstly you may sure that in CallcenterBillingsController you have CallcenterBilling model in uses:
public $uses = array('CallcenterBilling');
Secondary you can optimize this by saving not in loop:
see saveAll method of the model
so you will get something like:
class CallcenterBillingController extends AppController {
public $uses = array('CallcenterBilling');
public function someaction() {
$data = array();
foreach ($list as $l) {
$data[] = array(
'source_number' => $l['Csv']['Source'],
'destination_number' => $l['Csv']['Destination'],
'seconds' => $l['Csv']['Seconds'],
'callerID' => $l['Csv']['CallerID'],
'disposition' => $l['Csv']['Disposition'],
'cost' => $l['Csv']['Cost'],
'billing_cost' => $l['Csv']['newCost']
);
//$total[] = $l['Csv']['newCost'];
}
debug($data);
$this->CallcenterBilling->saveAll($data);
}
}
ps. if this not helps, please show controller code.
You should implement this:
$this->CallcenterBilling->read(null, 1);
$this->CallcenterBilling->set(array(
'source_number'=> $l['Csv']['Source'],
'destination_number'=> $l['Csv']['Destination'],
'seconds'=> $l['Csv']['Seconds'],
'callerID'=> $l['Csv']['CallerID'],
'disposition'=> $l['Csv']['Disposition'],
'cost'=> $l['Csv']['Cost'],
'billing_cost'=> $l['Csv']['newCost']
));
$this->CallcenterBilling->save();
This might works for you.

Resources