Here is a function which uses Zend DB / Tablegateway :
public function listAttestations($sSidx = null, $sSord = null, $iOffset = 0, $iLimit = 0)
{
try {
$resultSet = $this->tableGateway->select(
function (Select $select) use ($sSidx, $sSord, $iOffset, $iLimit) {
if ($sSidx != null && $sSord != null) {
$select->order($sSidx.' '.$sSord);
}
$select->join(
'f_travclient',
'syndic_client_id = f_travclient.travClient_id',
array('syndic' => 'nom')
);
$select->offset($iOffset);
$select->limit($iLimit);
}
);
return $resultSet;
} catch (\Exception $e) {
throw new \Exception($e);
}
}
I use PHPUnit to do unit tests. Perhaps, I don't know how to make the function which crosses my previous method. I thought this could be functional :
public function testListAttestations()
{
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with()
->will($this->returnValue($resultSet));
$attestTable = new FMaiAttestationTable($mockTableGateway, $this->adapter, $this->sql);
$this->assertSame($resultSet, $attestTable->listAttestations('maiAttestation_id', 'ASC', 0, 30));
}
But this doesn't go further the :
function (Select $select) use ($sSidx, $sSord, $iOffset, $iLimit) {
Could somebody help me ? Thanks.
You can fetch and use any arguments given to a mocked method with returnCallback():
$mockTableGateway->expects($this->once())
->method('select')
->with()
->will($this->returnCallback(function($function) use ($resultSet) {
// do something with the function, for example:
if(!is_callable($function)) {
return NULL;
}
call_user_func($function, new Select());
return $resultSet;
}));
Still, you might want to reconsider your current code as it is not necessary to write it nested like this. You could, for example, get an instance of Select yourself and use it with selectWith()
public function listAttestations($sSidx = null, $sSord = null, $iOffset = 0, $iLimit = 0)
{
try {
$select = new Select();
if ($sSidx != null && $sSord != null) {
$select->order($sSidx.' '.$sSord);
}
$select->join(
'f_travclient',
'syndic_client_id = f_travclient.travClient_id',
array('syndic' => 'nom')
);
$select->offset($iOffset);
$select->limit($iLimit);
$resultSet = $this->tableGateway->selectWith($select);
return $resultSet;
} catch (\Exception $e) {
throw new \Exception($e);
}
}
In this case it is now possible to test if your method puts the Select together the way you want. You can simply create another Select object in your test the way you expect it to be:
$resultSet = new ResultSet();
$sSidx = 'maiAttestation_id';
$sSord = 'ASC';
$iOffset = 0;
$iLimit = 30;
$select = new Select();
$select->order($sSidx.' '.$sSord);
$select->join(
'f_travclient',
'syndic_client_id = f_travclient.travClient_id',
array('syndic' => 'nom')
);
$select->offset($iOffset);
$select->limit($iLimit);
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway',
array('selectWith'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('selectWith')
->with($select)
->will($this->returnValue($resultSet));
$attestTable = new FMaiAttestationTable($mockTableGateway, $this->adapter, $this->sql);
$this->assertEquals($resultSet, $attestTable->listAttestations($sSidx, $sSord, $iOffset, $iLimit));
If the Select object used by the mocked method selectWith looks any different from the one you created, PHPUnit will throw an error in your test.
Related
try {
\DB::beginTransaction();
Model::updateOrCreate( ['id' => $id, 'number' => $number],
['value' => $request->get('value')]
);
\DB::commit();
} catch (\Exception $e) {
\DB::rollBack();
throw new \Exception($e->error());
}
I was working on a task to create a common trait to prevent a record to be updated by multiple users at the same time. And, my method was to put hidden input of $data->updated_at in a blade and then to check it when an update request is sent. And there are some cases Laravel's updateOrCreate is used to update or create a new record. And, I don't know how to deal with that. Should, I split the methods to create and update or is there any good way to deal with it?
You probably want to separate what you are doing.
I was playing around and came up with this for fun (haven't tested):
Illuminate\Database\Eloquent\Builder::macro('createOrUpdateWhen', function ($attributes = [], $values = [], $when = null) {
$m = $this->firstOrNew($attributes);
if (is_array($when)) {
$when = function ($m) use ($when) {
foreach ($when as $key => $value) {
if ($m->$key != $value) {
return false;
}
}
return true;
}
}
if (! $m->exists || $when($m)) {
$m->fill($values);
}
$m->save();
return $m;
});
$m = M::createOrUpdateWhen($attributes, $values, function ($m) use ($request) {
return $m->updated_at == $request->input('updated_at');
});
$m = M::createOrUpdateWhen(
$attributes, $values, ['updated_at' => $request->input('updated_at')]
);
:-}
$post = Model::where([['id', $id], ['number', $number]])->first();
try {
\DB::beginTransaction();
if (is_null($post)) {
Model::create($input);
} else {
$post->updateWithExclLock($request->get('updated_at'), $input]);
}
This is my function for which I am trying to test if it throws an exception or not:
public function myFunction(): bool
{
$dateInterval = new \DateTime();
$dateInterval->sub(new \DateInterval('PT24H'));
/** #var \PDOStatement $stmt */
$stmt = $this->pdo->prepare('SELECT SUM(`values`) FROM `event_tracker` WHERE `identifier` = ? AND `mutated_at` >= ? AND `source` = ? AND `customer` = ?');
$stmt->execute([$this->identifier, $dateInterval->getTimestamp(), $this->source, $this->customer]);
$sum = $stmt->fetchColumn(0);
if ($this->checkSourceType($this->source) && $sum >= $this->amountLimitCMS[$this->storeId] ){
$this->exceptionMessage($this->amountLimitCMS[$this->countryIso], $dateInterval->format('H:i d-m-Y'), $this->source);
}
if (!$this->checkSourceType($this->source) && $sum >= $this->amountLimitMagento[$this->storeId] ){
$this->exceptionMessage($this->amountLimitMagento[$this->storeId], $dateInterval->format('H:i d-m-Y'), $this->source);
}
return true;
}
This is my unitTest function:
public function testAmountCheckForCMS()
{
$query = [
'store_id' => 13,
'shipping_countryiso2' => 'DK',
'amount' => 4000,
];
$customer = '000003090';
$source = 'fulfillment_prod';
$container = new Container();
$container['db'] = function ($container) {
return $this->createMock(\PDO::class);
};
$dateInterval = new \DateTime();
$dateInterval->sub(new \DateInterval('PT24H'));
$ordersPerCustomer = new AmountPerCustomer($container, $customer, $query, $source);
$fetchAllMock = $this
->getMockBuilder('PDOStatement')
->setMethods(['execute'])
->getMock();
$fetchAllMock
->expects($this->once())->method('fetchColumn')
->will($this->returnValue($query['amount']));
try {
$ordersPerCustomer->assertPassedCriteria();
$this->fail("Expected Exception has not been raised.");
}catch (\Exception $error) {
$this->assertEquals($error->getMessage(), "Total order amount event given parameters exceed sum {$query['amount']} since {$dateInterval->format('H:i d-m-Y')} from source {$source}");
}
}
As you in see, in my function, which I would like to test, the execute and fetchColumn functions are used. How can I mock them ? Right now, when I run my tests I am getting this error message:
Trying to configure method "fetchColumn" which cannot be configured because it does not exist, has not been specified, is final, or is static
Any idea how can I fix this ? Thank you!
I know I am late here but you need to set that method in mockBuilder so that it can be mocked.
here is the sample code -
$this->getMockBuilder(PDOStatement::class)
->disableOriginalConstructor()
->setMethods(["fetchColumn"])
->getMock();
I want to short this function. I have also published a little part of it, but it is every time the same principle:
if(in_array($infinitiveVerb,
IrregularExceptionGroup::$name_in_lowercase)) {
$exceptionmodel = ExceptionModel::NAME_IN_UPPERCASE;
}
php function
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$exceptionmodel = ExceptionModel::NO_EXCEPTIONS;
if (in_array($infinitiveVerb, IrregularExceptionGroup::$aller)) {
$exceptionmodel = ExceptionModel::ALLER;
}
if (in_array($infinitiveVerb, IrregularExceptionGroup::$avoir_irr)) {
$exceptionmodel = ExceptionModel::AVOIR_IRR;
}
if (in_array($infinitiveVerb, IrregularExceptionGroup::$etre_irr)) {
$exceptionmodel = ExceptionModel::ETRE_IRR;
}
return new ExceptionModel($exceptionmodel);
}
ExceptionModel.php
class ExceptionModel extends Enum
{
const NO_EXCEPTIONS = 'no exceptions';
const ALLER = 'aller';
const AVOIR_IRR = 'avoir_irr';
const ETRE_IRR = 'etre_irr';
}
How is this possible?
The only thing I can see and would change here, is to just put every irregularExceptionGroup into an array, like this:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$exceptionmodel = ExceptionModel::NO_EXCEPTIONS;
$irregularExceptionGroupArray = [
ExceptionModel::ALLER => IrregularExceptionGroup::$aller,
ExceptionModel::AVOIR_IRR => IrregularExceptionGroup::$avoir_irr,
ExceptionModel::ETRE_IRR => IrregularExceptionGroup::$etre_irr,
];
foreach($irregularExceptionGroupArray as $exceptionModel => $irregularExceptionGroup){
if(in_array($infinitiveVerb, $irregularExceptionGroup)){
$exceptionmodel = $exceptionModel;
//break; //If you don't want to overwrite the variable, just uncomment this
}
}
return new ExceptionModel($exceptionmodel);
}
You can combine all exceptions into one lookup, instead of this in_array checks all the time. (Note: IF off course the InfinitVerb has a __toString of some kind)
For example IrregularExceptionGroup::$aller contains: ['aller', 'allez', 'je suis', 'paris'] and IrregularExceptionGroup::$avoir_irr contains ['some', 'more', 'stuff']
Change it to:
IrregularExceptionGroup::$allExceptions = [
'aller' => ExceptionModel::ALLER,
'allez' => ExceptionModel::ALLER,
'je suis' => ExceptionModel::ALLER,
'paris' => ExceptionModel::ALLER,
'some' => ExceptionModel::AVOIR_IRR,
'more' => ExceptionModel::AVOIR_IRR,
'stuff' => ExceptionModel::AVOIR_IRR
];
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
$ex = ExceptionModel::NO_EXCEPTIONS;
if (array_key_exists($infinitiveVerb, IrregularExceptionGroup::$allExceptions)) {
$ex = IrregularExceptionGroup::$allExceptions[$infinitiveVerb];
}
return new ExceptionModel($ex);
}
And you could even make it "shorter" by using the ternary operator:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
return new ExceptionModel(isset(IrregularExceptionGroup::$allExceptions[$infinitiveVerb]) ? IrregularExceptionGroup::$allExceptions[$infinitiveVerb] : ExceptionModel::NO_EXCEPTIONS);
}
or PHP 7:
function finding_exception_model(InfinitiveVerb $infinitiveVerb)
{
return new ExceptionModel( IrregularExceptionGroup::$allExceptions[$infinitiveVerb] ?? ExceptionModel::NO_EXCEPTIONS);
}
I'm a little bit baffled by this, so I'm hoping someone can shed some light on it for me.
I have a function which is meant to return a column Status from one of my tables, visit.
function someFunction($visitId = null) {
$visit = VisitQuery::create()
->select(array('Status'))
->findPk($visitId);
}
If I var_dump($visit), when calling the function for the first time, it outputs:
string '1' (length=1)
Subsequent, identical calls to the function however seem to return an entire object:
object(Visit)[30]
protected 'startCopy' => boolean false
protected 'id' => int 362
protected 'job_id' => int 351
protected 'company_id' => int 2
protected 'type_id' => int 1
protected 'visit_date' => string '2013-08-23 00:00:00' (length=19)
protected 'status' => string '1' (length=1)
...
I'm calling the function for the first time with an (int) $visitId passed via a posted form:
var_dump($visitId); // int 362
Subsequent calls are made with an (int) $visitId which is returned from another function, saveVisit() (which uses Propel to save the record - I believe this may have something to do with it).
$visitId = saveVisit($visitId);
var_dump($visitId); // int 362
I tried to do some debugging, and for some reason the query issued to MySQL is different between the first function call and subsequent ones:
var_dump($con->getLastExecutedQuery());
SELECT visit.STATUS AS "Status" FROM `visit` WHERE visit.ID=362 // 1st call
SELECT `ID`, `JOB_ID`, `COMPANY_ID`, `TYPE_ID`, `VISIT_DATE`, `STATUS`, `REMIND`, `PRINTED` FROM `visit` WHERE `ID` = 362 // 2nd call
SELECT `ID`, `JOB_ID`, `COMPANY_ID`, `TYPE_ID`, `VISIT_DATE`, `STATUS`, `REMIND`, `PRINTED` FROM `visit` WHERE `ID` = 362 // 3rd call
Can anyone tell me why or how this is happening?
I'm using Propel 1.6.
Propel's create() method:
public static function create($modelAlias = null, $criteria = null)
{
if ($criteria instanceof VisitQuery) {
return $criteria;
}
$query = new VisitQuery();
if (null !== $modelAlias) {
$query->setModelAlias($modelAlias);
}
if ($criteria instanceof Criteria) {
$query->mergeWith($criteria);
}
return $query;
}
Propel's findPk() method:
public function findPk($key, $con = null)
{
if ($con === null) {
$con = Propel::getConnection($this->getDbName(), Propel::CONNECTION_READ);
}
// As the query uses a PK condition, no limit(1) is necessary.
$this->basePreSelect($con);
$criteria = $this->isKeepQuery() ? clone $this : $this;
$pkCols = $this->getTableMap()->getPrimaryKeyColumns();
if (count($pkCols) == 1) {
// simple primary key
$pkCol = $pkCols[0];
$criteria->add($pkCol->getFullyQualifiedName(), $key);
} else {
// composite primary key
foreach ($pkCols as $pkCol) {
$keyPart = array_shift($key);
$criteria->add($pkCol->getFullyQualifiedName(), $keyPart);
}
}
$stmt = $criteria->doSelect($con);
return $criteria->getFormatter()->init($criteria)->formatOne($stmt);
}
My function to retrieve the visit status:
function getVisitStatus($visitId = null) {
if (empty($visitId)) {
return false;
}
try {
$visit = VisitQuery::create()
->select(array('Status'))
->findPk($visitId);
} catch (Exception $e) {
echo $e->getMessage(); exit;
}
if (is_null($visit)) {
return false;
}
return $visit;
}
The function which saves a visit record:
function saveVisit($data = null) {
if (empty($data)) {
return false;
}
try {
$visit = (!empty($data['visit_id'])) ? VisitQuery::create()->findPk($data['visit_id']) : new Visit();
if (!is_object($visit)) {
return false;
}
$visitDataMap = array(
'JobId' => 'job_id',
'TypeId' => 'type_id',
'VisitDate' => 'visit_date',
'Status' => 'status',
'CompanyId' => 'engineer_id',
'Remind' => 'remind'
);
$visitData = array();
foreach ($visitDataMap as $column => $value) {
if (!empty($data[$value])) {
$visitData[$column] = $data[$value];
}
}
$visit->fromArray($visitData);
$visit->save();
return $visit->getId();
} catch (PropelException $e) {
echo $e->getMessage(); exit;
}
}
It seems that on the first call will grab your data but then put a copy of the full object in to the instance pool. I am not sure if this is a bug or valid behaviour (your code would suggest the former, but I'd love to hear from someone who knows more about Propel such as a dev) but you can stop it happening with:
VisitPeer::clearInstancePool();
Bear in mind you're losing a bit of caching from other queries you might have done here with the visit relation.
You will be able to confirm this by adding an echo in the BaseVisitPeer.php file. You will see something like this:
public static function getInstanceFromPool($key)
{
if (Propel::isInstancePoolingEnabled()) {
if (isset(VisitPeer::$instances[$key])) {
return VisitPeer::$instances[$key];
}
}
return null; // just to be explicit
}
If you add an echo somewhere inside the if (isset(VisitPeer::$instances[$key])) { statement, you should see it appear only after the second and subsequent calls. If you were to comment this whole if statement out then you would get the same result back each time - the one you correctly get back from your original call.
Hope that helps!
I would like to create the following using class syntax:
$resp = new stdclass;
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
Should be easy but I cannot find the syntax for this.
I will later output $resp in json format. I am aware I can also use arrays for this...
The json output shall be:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
You can also make your classes more explicit:
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
}
class CategoryListResp {
public $category = array();
}
$resp = new Resp();
$resp->categoryListResp->category[0]->categoryId = 1;
$resp->categoryListResp->category[0]->categoryName = "Spel";
$resp->categoryListResp->category[0]->iconUri = "PictoSpel.png";
// etc.
ADDED (based on henq's comment). To fully utilize the class concept you would need to add some methods to the classes. Then you would not use -> for arrays, but call the respective methods. E.g.
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
public function __construct($id, $name, $icon) {
$this->categoryId = $id;
$this->categoryName = $name;
$this->iconUri = $icon;
}
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
public function addCategory($index, $id, $name, $icon) {
$this->categoryListResp->addCategory($index, $id, $name, $icon);
}
}
class CategoryListResp {
public $category = array();
public function addCategory($index, $id, $name, $icon) {
$this->category[$index] = new Category($id, $name, $icon);
}
}
$resp = new Resp();
$resp->addCategory(0, 1, "Spel", "PictoSpel.png");
$resp->addCategory(1, 2, "Transport", "PictoTransport.png");
// etc
You can modify this concept according to your needs.
You're almost there already:
$resp = new stdClass();
$resp->CategoryListResp = new stdClass();
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
print_r(json_encode($resp));
/*
output:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
*/
Just send $resp to json_encode. Your code should work as is, however. It's better design to create class definitions for CategoryListResp and Category, rather than just using stdClass.
Arrays are the simpler way to go (as suggested by #felix-kling)
This is how the code ended up:
$resp = array(
'CategoryListResp' => array(
'category' => array(
array(
'categoryId' => 1,
'categoryName' => 'Spel',
'iconUri' => 'PictoSpel.png'
),
array(
'categoryId' => 2,
'categoryName' => 'Transport',
'iconUri' => 'PictoTransport.png'
),
),
),
);
print json_encode($resp);
Clean and simple.