Mock databases function from drupal 7 - 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!

Related

RESOLVED cakephp 4 - Test mail in command

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

CakePHP 4.2 Annotate: incorrect app namespace

I'm using CakePHP version 4.2 and am noticing some odd behavior from the annotate script that comes bundled with the API. For one component, the annotate script wants to default to the App\ domain that is CakePHP's default. I've changed the application name so most other classes default to the correct application name. But not this one script and so far, only for this one file.
I've included the body of the component, for review, below. You can see that the #method annotation uses the App\ domain. The trouble comes in when I use PHPStan to analyze my code. If I leave the annotation as is, PHPStan will tell me:
------ --------------------------------------------------------------------------------------------------------------------------------------------------------------
Line src/Controller/Component/CartManagerComponent.php
------ --------------------------------------------------------------------------------------------------------------------------------------------------------------
43 Property Visualize\Controller\Component\CartManagerComponent::$Controller (Visualize\Controller\AppController) does not accept App\Controller\AppController.
44 Call to method loadModel() on an unknown class App\Controller\AppController.
💡 Learn more at https://phpstan.org/user-guide/discovering-symbols
------ --------------------------------------------------------------------------------------------------------------------------------------------------------------
The file itself doesn't use the App\ domain anywhere. I'm not sure where to look for the script to figure out whats wrong. Here is the body of my component in case you see something I do not:
<?php
declare(strict_types=1);
namespace Visualize\Controller\Component;
use Authorization\Identity;
use Cake\Controller\Component;
use Cake\Log\Log;
/**
* CartManager component
*
* #method \App\Controller\AppController getController()
* #property \Visualize\Controller\AppController $Controller
* #property \Visualize\Model\Table\CartsTable $Carts
*/
class CartManagerComponent extends Component
{
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
/**
* #var \Visualize\Controller\AppController
*/
protected $Controller;
/**
* #var \Visualize\Model\Table\CartsTable
*/
protected $Carts;
/**
* #param array $config The current configuration array
* #return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
$this->Controller = $this->getController();
$this->Controller->loadModel('Carts');
}
/**
* Returns the most recent active cart.
*
* #param \Authorization\Identity $user The User entity.
* #return array|\Cake\Datasource\EntityInterface|null
* #noinspection PhpUnnecessaryFullyQualifiedNameInspection
*/
public function getUserCart(Identity $user)
{
$cart = $this->Controller->Carts->newEmptyEntity();
if (!empty($this->Controller->Carts) && is_a($this->Controller->Carts, '\Visualize\Model\Table\CartsTable')) {
$query = $this->Controller->Carts->find('userCart', ['user_id' => $user->getIdentifier()]);
if (!$query->isEmpty()) {
$cart = $query->first();
} else {
$cart->set('user_id', $user->getIdentifier());
$this->Controller->Carts->save($cart);
}
if (is_object($cart) && is_a($cart, '\Cake\Datasource\EntityInterface')) {
$session = $this->Controller->getRequest()->getSession();
$session->write('Cart.id', $cart->id);
}
}
return $cart;
}
/**
* Abandons carts
*
* #param int $user_id The associated user ID
* #param int $cart_id The current cart ID
* #return void
*/
public function pruneCarts(int $user_id, int $cart_id): void
{
if (!empty($this->Controller->Carts) && is_a($this->Controller->Carts, '\Visualize\Model\Table\CartsTable')) {
// Find all the carts we didn't just create:
$userCarts = $this->Controller->Carts->find('all', ['fields' => ['id', 'user_id', 'cart_status']])
->where([
'id !=' => $cart_id,
'user_id' => $user_id,
'cart_status' => 'active',
]);
if (!$userCarts->isEmpty()) {
$count = 0;
foreach ($userCarts as $cart) {
if ($count < 5) {
$record = $this->Controller->Carts->newEmptyEntity();
$record = $this->Controller->Carts->patchEntity($record, $cart->toArray());
$record->set('id', $cart->id);
$record->set('cart_status', ABANDONED_CART);
if (!$this->Controller->Carts->save($record)) {
Log::alert('Error abandoning cart');
}
} else {
$this->Controller->Carts->delete($cart);
}
$count++;
}
}
}
}
}

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.

load external class and member function in Cakephp 3

I'm working on CakePHP 3.2.
I want to import data in bulk from excel file and save them to database. For this I'm using PHPExcel Library.
I have downloaded the library and extracted in vendor directory and thus the filepath to PHPExcel.php is
/vendor/PHPExcel/Classes/PHPExcel.php
and filepath to IOFactory.php is
/vendor/PHPExcel/Classes/PHPExcel/IOFactory.php
I'm including this in my controller like
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\Event;
include '../vendor/PHPExcel/Classes/PHPExcel.php';
include '../vendor/PHPExcel/Classes/PHPExcel/IOFactory.php';
/**
* Products Controller
*
* #property \App\Model\Table\ProductsTable $Products
*/
class ProductsController extends AppController
{
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
if ($this->Auth->user()['status'] != 1) {
$this->Auth->deny(['sell']);
}
$this->Auth->allow(['bulkUpload']);
}
public function bulkUpload()
{
$inputFileName = $this->request->data('excel_data');
if ($inputFileName != '') {
$inputFileType = PHPExcel_IOFactory::identify($inputFileName); // line 33
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load($inputFileName);
$objWorksheet = $objPHPExcel->setActiveSheetIndex(0);
$highestRow = $objWorksheet->getHighestRow();
for ($row = 2; $row <= $highestRow; ++$row) {
$this->data['Program']['cycle_month'] = $objWorksheet->getCellByColumnAndRow(1, $row)->getValue();
$this->data['Program']['cycle_year'] = $objWorksheet->getCellByColumnAndRow(2, $row)->getValue();
$this->data['Program']['media_partnum'] = $objWorksheet->getCellByColumnAndRow(3, $row)->getValue();
$resultArray[$row-2] = $this->data['Program'];
}
debug($resultArray);
}
}
}
Note : I have never used such plugin and bulk upload that is why I followed code from This Question on StackOverflow
Now, the problem is, When I select a file and upload, it gives error as
Class 'App\Controller\PHPExcel_IOFactory' not found at line 33
I think the problem is with calling PHPExcel_IOFactory class.
PHPExcel_IOFactory class is inside IOFactory.php file
<?php
/** PHPExcel root directory */
if (!defined('PHPEXCEL_ROOT')) {
/**
* #ignore
*/
define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../');
require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
}
class PHPExcel_IOFactory
{
/**
* Search locations
*
* #var array
* #access private
* #static
*/
private static $searchLocations = array(
array( 'type' => 'IWriter', 'path' => 'PHPExcel/Writer/{0}.php', 'class' => 'PHPExcel_Writer_{0}' ),
array( 'type' => 'IReader', 'path' => 'PHPExcel/Reader/{0}.php', 'class' => 'PHPExcel_Reader_{0}' )
);
/**
* Autoresolve classes
*
* #var array
* #access private
* #static
*/
private static $autoResolveClasses = array(
'Excel2007',
'Excel5',
'Excel2003XML',
'OOCalc',
'SYLK',
'Gnumeric',
'HTML',
'CSV',
);
/**
* Private constructor for PHPExcel_IOFactory
*/
private function __construct()
{
}
public static function identify($pFilename)
{
$reader = self::createReaderForFile($pFilename);
$className = get_class($reader);
$classType = explode('_', $className);
unset($reader);
return array_pop($classType);
}
}
I see it's a simple problem of namespaces. Just put a slash before the name class as it is in the global namespace
$inputFileType = \PHPExcel_IOFactory::identify($inputFileName);
You can use composer to load PHPExcel library. It will auto include all classes in your project.
composer require phpoffice/phpexcel
Cheers :)

Yii record is not inserting into DB

Below is my controller & model logic - I just started a barebones Yii installation to play around with it more.
I get no errors but don't see the new entry in the database - my db has been configured in the main.php (this works as Gii runs).
// controllers/PageController.php
class PageController extends Controller
{
public function actionSave($value='')
{
$pageObj = new Page;
$pageObj->savePage();
}
}
// models/Page.php
class Page extends CActiveRecord
{
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'page';
}
/**
* #return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('title, date_updated', 'required'),
array('live', 'numerical', 'integerOnly'=>true),
array('user_id', 'length', 'max'=>10),
array('title', 'length', 'max'=>100),
array('content, date_published', 'safe'),
// The following rule is used by search().
// #todo Please remove those attributes that should not be searched.
array('id, user_id, live, title, content, date_updated, date_published', 'safe', 'on'=>'search'),
);
}
/**
* #return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'comments' => array(self::HAS_MANY, 'Comment', 'page_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'files' => array(self::MANY_MANY, 'File', 'page_has_file(page_id, file_id)'),
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'ID',
'user_id' => 'User',
'live' => 'Live',
'title' => 'Title',
'content' => 'Content',
'date_updated' => 'Date Updated',
'date_published' => 'Date Published',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
*
* Typical usecase:
* - Initialize the model fields with values from filter form.
* - Execute this method to get CActiveDataProvider instance which will filter
* models according to data in model fields.
* - Pass data provider to CGridView, CListView or any similar widget.
*
* #return CActiveDataProvider the data provider that can return the models
* based on the search/filter conditions.
*/
public function search()
{
// #todo Please modify the following code to remove attributes that should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id,true);
$criteria->compare('user_id',$this->user_id,true);
$criteria->compare('live',$this->live);
$criteria->compare('title',$this->title,true);
$criteria->compare('content',$this->content,true);
$criteria->compare('date_updated',$this->date_updated,true);
$criteria->compare('date_published',$this->date_published,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* #param string $className active record class name.
* #return Page the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function savePage($value='')
{
$page = new page;
$model->isNewRecord = true;
$model->primaryKey = NULL;
$page->title='sample page';
$page->content='content for the sample page';
$page->save(false);
}
}
In Yii, when you want to insert into a table which has some null columns, you must put null columns in your rules as SAFE like below:
array('primaryKey','safe'),
Now, Yii knows that primaryKey is a null column. So, there would be no problem via inserting into the current model.
As a note, when you call save() method with FALSE, you are telling to your model to do not the validation on insert.
Also, the correct way to skip possible errors is to validate your model before inserting like below:
if($model->validate()){
// VALIDATE, YOU CAN CALL SAVE FUNCTION
}else{
//here you can send an error message via FLASH or you can debug what the exact error is like below:
CVarDumper::dump($model->getErrors(),5678,true);
Yii::app()->end();
}
I hope, it help
So simple... I hate Yii sometimes :-)
Had to set the save() to save(false)
$page = new page;
$page->isNewRecord = true;
$page->primaryKey = NULL;
$page->title='sample page';
$page->content='content for the sample page';
$page->save(false);
Thanks for that - I had missed out some columns.. (silly me)
Improve the function even further with the help above..
public function savePage()
{
$page = new page;
$page->isNewRecord = true;
$page->primaryKey = NULL;
$page->user_id = 1;
$page->live = 0;
$page->content='content for the sample page';
$page->date_updated = date('Y-m-d H:i:s');
$page->date_published = date('Y-m-d H:i:s');
$page->title='sample page';
if ($page->validate()) {
$page->save();
} else {
CVarDumper::dump($page->getErrors(),5678,true);
Yii::app()->end();
}
}

Resources