using Cakephp Restful WS with primary key different from default 'id' - web-services

I want to create a Webservice in cakephp but the primary key is not id_supp but taken the default value id
this is the modal:
<?php
App::uses('AppModel', 'Model');
class Supplier extends AppModel {
var $primaryKey = 'id_supp';
this is the route
Router::mapResources(array('suppliers'));
and this is the view action
public function view($id) {
$supplier = $this->Supplier->findById($id);
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
The result when accessing the following url via GET
/suppliers/54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702.json
is:
{
"code": 500,
"url": "\/TN\/Back_rest\/suppliers\/54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702.json",
"name": "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Supplier.id' in 'where clause'",
"error": {
"errorInfo": [
"42S22",
1054,
"Unknown column 'Supplier.id' in 'where clause'"
],
"queryString": "SELECT `Supplier`.`id_supp`, `Supplier`.`company_name`, `Supplier`.`contact_name`, `Supplier`.`contact_title`, `Supplier`.`address`, `Supplier`.`postcode`, `Supplier`.`phone`, `Supplier`.`fax`, `Supplier`.`www`, `Supplier`.`active`, `Supplier`.`created`, `Supplier`.`modified` FROM `tn`.`suppliers` AS `Supplier` WHERE `Supplier`.`id` = '54f4dc83-0bd0-4fdd-ab8b-0a08ba3b5702' LIMIT 1"
}}

Because cakephp uses convention over configuration you should use id for your table primary id field. In your example you could find what you are looking for like this:
public function view($id = null) {
$supplier = $this->Supplier->find('first', array(
'conditions' => array(
'Supplier.id_supp' => $id
)
));
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
or like this:
public function view($id = null) {
$this->Supplier->primaryKey = $id;
$supplier = $this->Supplier->find('first');
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
or like this:
public function view($id = null) {
$supplier = $this->Supplier->findByIdSupp($id);
$this->set(array(
'supplier' => $supplier,
'_serialize' => array('supplier')
));
}
Choose what ever pleases you the most.

Related

Unable to save new "ActiveRecord" in database through Codeception

I have a simple Unit test in which I'm trying to create a new record in "orders" table . So when the test is run it throws an exception :
[yii\db\IntegrityException] SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'created_by' cannot be null
I guess this is due to the so called "BlamemableBehavior" trying to update the "created_by" column . So I tried to detach it and to manually pass the "created_by" value . Neither of both worked . Can you please help ?
<?php
namespace frontend\tests\unit\models;
// use common\fixtures\UserFixture;
use frontend\components\Payments;
use common\models\order\Order;
class RandomTest extends \Codeception\Test\Unit
{
/**
* #var \frontend\tests\UnitTester
*/
protected $tester;
protected $order;
public function _before(){
//this doesn't work
$this->order = $this->tester->haveRecord(Order::class,
[
'id' => 577,
'payment_type' => 4,
'status' => 1,
'amount' => 1,
'created_by' => 561,
'updated_by' => 561,
]);
}
public function testRandom(){
//this does not work either
/*$model = new Order;
$model->detachBehavior('BlameableBehavior');
$model->payment_type = 4;
$model->status = 1;
$model->amount = 1;
$model->created_by = 561;
$model->updated_by = 561;
$model->save();*/
}
}
The "Order" model :
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors[] = [
'class' => \common\components\behaviors\modelLog\ActiveRecordHistoryBehavior::className(),
'manager' => '\common\components\behaviors\modelLog\managers\DBManager',
'ignoreFields' => ['updated_at', 'created_at']
];
return $behaviors;
}
/**
* #inheritdoc
*/
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['paid'] = [
'status', 'invoice_number', 'paid_date', 'is_used', 'allocation'];
return $scenarios;
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['status', 'payment_type', 'invoice_reference', 'invoice_number'], 'integer'],
[['payment_type', 'amount', 'vat', 'total_amount', 'credit', 'invoice_reference'], 'required'],
[['amount', 'vat', 'total_amount', 'credit', 'discount'], 'number'],
[['reason'], 'trim'],
[['allocation'], 'string']
];
}

Coupon Magento API Soap

I’ve a problem with the Coupon API when i make :
$couponCode = "test";
$resultCartCoupon = $proxy->call($sessionId, "cart_coupon.add", array($shoppingCartId, $couponCode));
I always got : Uncaught SoapFault exception: [1083] Coupon is not valid if i try the coupon code in the front end there is no problem. Is there anyone who have ever used this API part with success ?
Thanks.
This error comes from Mage_Checkout_Model_Cart_Coupon_Api::_applyCoupon()
if ($couponCode) {
if (!$couponCode == $quote->getCouponCode()) {
$this->_fault('coupon_code_is_not_valid');
}
}
This looks like it could be a bug, instead it should be if ($couponCode != $quote->getCouponCode()) { but I'm not certain.
It could be that the cart (quote) you're trying to apply the coupon to isn't valid, i.e. doesn't have the qualifying items it needs to receive the coupon. Are you sure $shoppingCartId correctly matches the expected quote in Magento's sales_flat_quote table?
I noticed that the error is in this excerpt:
try {
$quote->getShippingAddress()->setCollectShippingRates(true);
$quote->setCouponCode(strlen($couponCode) ? $couponCode : '')
->collectTotals()
->save();
} catch (Exception $e) {
$this->_fault("cannot_apply_coupon_code", $e->getMessage());
}
In this specific line: ->collectTotals() By removing this stretch , not of error , but not applied the coupon.
After debugging 2-3 hour on API, I have solved this error at my-end. Check below code which i have used in Coupon API.
<?php
$mage_url = 'http://yoursiteurl/api/soap?wsdl';
$mage_user= "API_User"; //webservice user login
$mage_api_key = "API_PASSWORD"; //webservice user pass
$client = new SoapClient($mage_url);
$couponCode = 'couponCode'; // a coupon to be apply
$shoppingCartId = '35'; // a cart Id which i have put test id
$sessionId = $client->login($mage_user, $mage_api_key);
$result = $client->call($sessionId,'cart_coupon.add',array($shoppingCartId,$couponCode));
print_r($result);
?>
The above code gives error that "Uncaught SoapFault exception: [1083] Coupon is not valid". When i debugg the core code i came to know that magento cart.create API insert wrong store id in sales_flat_quote table. I have changed the store id value in sales_flat_quote table manually and again run the Coupon API and after that it works perfectly. So here is the my solution. When you create cart id just run the below update query to change the store id.
<?php
$shoppingCartId = $soap->call( $sessionId, 'cart.create');
$mageFilename = '../app/Mage.php';
require_once $mageFilename;
umask(0);
Mage::app();
$db_write1 = Mage::getSingleton('core/resource')->getConnection('core_write');
$updateQue = "update sales_flat_quote set store_id='1' where entity_id ='".$shoppingCartId."'";
$db_write1->query($updateQue);
// Now run the Coupon API here
?>
Code taken from here : http://chandreshrana.blogspot.in/2015/11/uncaught-soapfault-exception-1083.html
You do not need to write direct SQL to resolve this issue. Just specify store ID parameter in the API call. Example is below is the demo script to apply discount code using Magento SOAP APIs V2 :
/* Set Discount Code */
try
{
$result = $client->shoppingCartCouponAdd($session, $quoteId, 'test123',$storeId);
echo "<br>Apply discount code: ";
var_dump($result);
}
catch(Exception $ex)
{
echo "<br>Discount code Failed: " . $ex->getMessage();
}
To apply discount code, perform following steps :
$quoteId = $client->shoppingCartCreate($session,$storeId);
/* Set cart customer */
$guest = true;
if ($guest)
{
$customerData = array(
"firstname" => "testFirstname",
"lastname" => "testLastName",
"email" => "testEmail#mail.com",
"mode" => "guest",
"website_id" => "1"
);
}
else
{
$customer = array(
"customer_id" => '69301',
"website_id" => "1",
"group_id" => "1",
"store_id" => "1",
"mode" => "customer",
);
}
//Set cart customer (assign customer to quote)
$resultCustomerSet = $client->shoppingCartCustomerSet($session, $quoteId, $customerData,$storeId);
/* Set customer addresses Shipping and Billing */
$addresses = array(
array(
"mode" => "shipping",
"firstname" => "Ahsan",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "Karachi",
"region" => "Sindh",
"postcode" => "7502",
"country_id" => "PK",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
),
array(
"mode" => "billing",
"firstname" => "Ahsan",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "Karachi",
"region" => "Sindh",
"postcode" => "7502",
"country_id" => "PK",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
)
);
//Set cart customer address
$resultCustomerAddress = $client->shoppingCartCustomerAddresses($session, $quoteId, $addresses,$storeId);
/* Set payment method */
$responsePayment = $client->shoppingCartPaymentMethod($session, $quoteId, array(
'method' => 'cashondelivery',
),$storeId);
/* Set shipping method */
$setShipping = $client->shoppingCartShippingMethod($session, $quoteId, 'flatrate_flatrate',$storeId);
After all above apply discount code,
try
{
$result = $client->shoppingCartCouponAdd($session, $quoteId, 'test123',$storeId);
echo "<br>Apply discount code: ";
var_dump($result);
}
catch(Exception $ex)
{
echo "<br>Discount code Failed: " . $ex->getMessage();
}

ZF2 - set selected value on Select Element

I've a problem with dropdown list with Zend Framework 2 & Doctrine.
I would put the "selected" attribute on my dropdown list but all options pass to selected
My code :
Controller :
public function editAction()
{
// get error message during addAction
$this->layout()->setVariable("messageError", $this->flashMessenger()->getErrorMessages());
$auth = $this->getAuthService();
if ($auth->hasIdentity()){
$builder = new AnnotationBuilder();
// Get id of StaticContent
$id = (int)$this->getEvent()->getRouteMatch()->getParam('id');
if (!$id) {
$this->flashMessenger()->addErrorMessage("Aucun plan choisi !");
return $this->redirect()->toRoute('admin/plans');
}
$plan = $this->getEntityManager()->getRepository("Admin\Entity\Plan")->find((int)$id);
$form = $builder->createForm($plan);
// Find options for Localite list (<select>)
$localites = $this->getEntityManager()->getRepository("Admin\Entity\Localite")->getArrayOfAll();
$form->get('localiteid')->setValueOptions($localites);
$form->get('localiteid')->setValue("{$plan->getLocaliteid()->getId()}");
// Find options for TypePlan list (<select>)
$typesPlan = $this->getEntityManager()->getRepository("Admin\Entity\TypePlan")->getArrayOfAll();
$form->get('typeid')->setValueOptions($typesPlan);
$form->get('typeid')->setValue("{$plan->getTypeid()->getId()}");
// Options for Statut list (<select>)
$form->get('statut')->setValueOptions(array('projet'=>'Projet', 'valide'=>'Validé'));
$form->get('statut')->setValue($plan->getStatut());
$form->setBindOnValidate(false);
$form->bind($plan);
$form->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Modifier',
'id' => 'submitbutton',
'class' => "btn btn-primary"
),
));
$request = $this->getRequest();
if ($request->isPost()) {
[...]
}
}
With
$localites = $this->getEntityManager()->getRepository("Admin\Entity\Localite")->getArrayOfAll();
$form->get('localiteid')->setValueOptions($localites);
i populate my dropdown correctly, normally with
$form->get('localiteid')->setValue("{$plan->getLocaliteid()->getId()}");
just set "selected" on option defined by :
$plan->getLocaliteid()->getId()
So why all options are selected in my dropdown ?!
Information : It's the same for typeId but no Statut
It's probably not working because of the curly braces. According to the PHP documentation
Using single curly braces ({}) will not work for accessing the return values of functions or methods or the values of class constants or static class variables.
This is also unnecessary when using setValue. ZF2 will convert it to a string when formatting it in the view.
When you create the arrays to pass to setValueOptions() you should make it an associative array of arrays with the following values:
$form->get('select')->setValueOptions(array(
'field' => array(
'value' => 'value_of_the_option',
'label' => 'what is displayed',
'selected' => true,
),
));
Which ever of the fields has the selected option set to true will be the default selection in the form element.
Personally i don't know if getArrayOfAll() such function exists, i assume that you are correctly passing array to FORM,
I think you should be doing something like this to set value.
$form->get('localiteid')->setValue($plan->getLocaliteid()->getId());
But Since you are populating DROP down i guess this approach will not work best with Drop Down. You need to do something like this
$form->get('localiteid')->setAttributes(array('value'=>$plan->getLocaliteid()->getId(),'selected'=>true));
I've found a bug ?!
$plan = $this->getEntityManager()->getRepository("Admin\Entity\Plan")->find((int)$id);
$idLocalite = 18;//(int)$plan->getLocaliteid()->getId();
$idTypePlan = 2;//(int)$plan->getTypeid()->getId();
When i'm using $plan->getLocaliteid()->getId(); or $plan->getTypeid()->getId() to pass parameter into Repository method getArrayOfAll($idLocalite)
LocaliteRepository.php :
class LocaliteRepository extends EntityRepository {
public function getArrayOfAll($currentLocaliteId) {
$result = $this->_em->createQuery("SELECT l.nom, l.localiteid FROM Admin\Entity\Localite l ORDER BY l.nom")->getArrayResult();
$localite = array();
foreach($result as $loc) {
if ($currentLocaliteId == $loc['localiteid']) {
$localite[$loc['localiteid']] = array(
'value' => $loc['localiteid'],
'label' => $loc['nom'],
'selected' => true,
);
} else {
$localite[$loc['localiteid']] = array(
'value' => $loc['localiteid'],
'label' => $loc['nom'],
'selected' => false
);
//$localite[$loc['localiteid']] = $loc['nom'];
}
}
return $localite;
}
}
So, if i'm using $idLocalite = 18 instead of $idLocalite = (int)$plan->getLocaliteid()->getId() only wanted option are selected. Why ?!

Doctrine findBy 'does not equal'

How do I do
WHERE id != 1
In Doctrine?
I have this so far
$this->getDoctrine()->getRepository('MyBundle:Image')->findById(1);
But how do I do a "do not equals"?
This maybe daft, but I cannot find any reference to this?
Thanks
There is now a an approach to do this, using Doctrine's Criteria.
A full example can be seen in How to use a findBy method with comparative criteria, but a brief answer follows.
use \Doctrine\Common\Collections\Criteria;
// Add a not equals parameter to your criteria
$criteria = new Criteria();
$criteria->where(Criteria::expr()->neq('prize', 200));
// Find all from the repository matching your criteria
$result = $entityRepository->matching($criteria);
There is no built-in method that allows what you intend to do.
You have to add a method to your repository, like this:
public function getWhatYouWant()
{
$qb = $this->createQueryBuilder('u');
$qb->where('u.id != :identifier')
->setParameter('identifier', 1);
return $qb->getQuery()
->getResult();
}
Hope this helps.
To give a little more flexibility I would add the next function to my repository:
public function findByNot($field, $value)
{
$qb = $this->createQueryBuilder('a');
$qb->where($qb->expr()->not($qb->expr()->eq('a.'.$field, '?1')));
$qb->setParameter(1, $value);
return $qb->getQuery()
->getResult();
}
Then, I could call it in my controller like this:
$this->getDoctrine()->getRepository('MyBundle:Image')->findByNot('id', 1);
Based on the answer from Luis, you can do something more like the default findBy method.
First, create a default repository class that is going to be used by all your entities.
/* $config is the entity manager configuration object. */
$config->setDefaultRepositoryClassName( 'MyCompany\Repository' );
Or you can edit this in config.yml
doctrine:
orm:
default_repository_class: MyCompany\Repository
Then:
<?php
namespace MyCompany;
use Doctrine\ORM\EntityRepository;
class Repository extends EntityRepository {
public function findByNot( array $criteria, array $orderBy = null, $limit = null, $offset = null )
{
$qb = $this->getEntityManager()->createQueryBuilder();
$expr = $this->getEntityManager()->getExpressionBuilder();
$qb->select( 'entity' )
->from( $this->getEntityName(), 'entity' );
foreach ( $criteria as $field => $value ) {
// IF INTEGER neq, IF NOT notLike
if($this->getEntityManager()->getClassMetadata($this->getEntityName())->getFieldMapping($field)["type"]=="integer") {
$qb->andWhere( $expr->neq( 'entity.' . $field, $value ) );
} else {
$qb->andWhere( $expr->notLike( 'entity.' . $field, $qb->expr()->literal($value) ) );
}
}
if ( $orderBy ) {
foreach ( $orderBy as $field => $order ) {
$qb->addOrderBy( 'entity.' . $field, $order );
}
}
if ( $limit )
$qb->setMaxResults( $limit );
if ( $offset )
$qb->setFirstResult( $offset );
return $qb->getQuery()
->getResult();
}
}
The usage is the same than the findBy method, example:
$entityManager->getRepository( 'MyRepo' )->findByNot(
array( 'status' => Status::STATUS_DISABLED )
);
I solved this rather easily (without adding a method) so i'll share:
use Doctrine\Common\Collections\Criteria;
$repository->matching( Criteria::create()->where( Criteria::expr()->neq('id', 1) ) );
By the way, i'm using the Doctrine ORM module from within Zend Framework 2 and i'm not sure whether this would be compatible in any other case.
In my case, i was using a form element configuration like this: to show all roles except "guest" in a radio button array.
$this->add([
'type' => 'DoctrineModule\Form\Element\ObjectRadio',
'name' => 'roles',
'options' => [
'label' => _('Roles'),
'object_manager' => $this->getEntityManager(),
'target_class' => 'Application\Entity\Role',
'property' => 'roleId',
'find_method' => [
'name' => 'matching',
'params' => [
'criteria' => Criteria::create()->where(
Criteria::expr()->neq('roleId', 'guest')
)
],
],
],
]);
I used the QueryBuilder to get the data,
$query=$this->dm->createQueryBuilder('AppBundle:DocumentName')
->field('fieldName')->notEqual(null);
$data=$query->getQuery()->execute();

using zend test dbadapter with zend db table abstract

Has anyone been able to to use Zend_Test_DbAdapter with Zend_Db_Table_Abstract?
I'm trying to test a Model I created that extends Zend_Db_Table_Abstract and I get an exception regarding the primary key not being set if I use a Zend_Test_DbAdapter (other adapters like mysql or sqlite) work fine.
class Model_Category extends Zend_Db_Table_Abstract
{
protected $_name = 'categories';
protected $_dependentTables = array('Model_Video');
public function getMap()
{
$map = array();
$rows = $this->fetchAll();
foreach($rows as $row)
{
$map[$row->id] = $row->name;
}
return $map;
}
}
Snippet from a PHPUnit test class:
protected function setUp()
{
$adapter = new Zend_Test_DbAdapter();
$stmt = Zend_Test_DbStatement::createSelectStatement(array(
array('id' => 1, 'name' => 'pranks'),
array('id' => 2, 'name' => 'physical_feats'),
array('id' => 3, 'name' => 'art'),
array('id' => 4, 'name' => 'cute'),
array('id' => 5, 'name' => 'philanthropy')
));
$adapter->appendStatementToStack($stmt);
$this->fixture = new Model_Category($adapter);
}
Exceptions are thrown when exercising the Model's methods:
public function testGetMap()
{
$expected = array(
'1' => 'pranks',
'2' => 'physical_feats',
'3' => 'art',
'4' => 'cute',
'5' => 'philanthropy'
);
$actual = $this->fixture->getMap();
$this->assertEquals($expected, $actual);
}
Results in:
Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: A table must have a primary key, but none was found
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:876
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:90
Forcing a primary key does not work either:
protected function setUp()
{
$adapter = new Zend_Test_DbAdapter();
$stmt = Zend_Test_DbStatement::createSelectStatement(array(
array('id' => 1, 'name' => 'pranks'),
array('id' => 2, 'name' => 'physical_feats'),
array('id' => 3, 'name' => 'art'),
array('id' => 4, 'name' => 'cute'),
array('id' => 5, 'name' => 'philanthropy')
));
$adapter->appendStatementToStack($stmt);
$this->fixture = new Model_Category(array(
'db' => $adapter,
'primary' => 'id'
));
}
Executing the same unit test, from above results in:
Model_CategoryTest::testGetMap()
Zend_Db_Table_Exception: Primary key column(s) (id) are not columns in this table ()
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:888
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:969
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:100
ZendFramework-1.10.6/library/Zend/Db/Table/Select.php:78
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1005
ZendFramework-1.10.6/library/Zend/Db/Table/Abstract.php:1303
application/models/Category.php:35
tests/unit/application/models/CategoryTest.php:93
You can define the primary key by doing the following on your Zend_Test_DbAdapter instance:
$adapter = new Zend_Test_DbAdapter();
$adapter->setDescribeTable('table_name', array('column_name' =>
array(
'SCHEMA_NAME' => 'schema_name',
'TABLE_NAME' => 'table_name'
'COLUMN_NAME' => 'column_name',
'PRIMARY' => true
)
));
And then transposing table_name, column_name and schema_name with the values from your implementation. You would need to do this for every table you are interacting with in the class under test.
The reason you are getting the exception Zend_Db_Table_Exception: A table must have a primary key, but none was found is because all tables that use Zend_Db_Table must have a primary key defined. When you go to use the table, since a primary key was not defined in your DbTable class, Zend_Db attempts to determine the table's primary key by examining the table's properties from the information schema. It sees your table doesn't have a primary key and fails.
From the manual:
If you don't specify the primary key, Zend_Db_Table_Abstract tries to
discover the primary key based on the information provided by the
describeTable() method.
Note: Every table class must know which columns can be used to address
rows uniquely. If no primary key columns are specified in the table
class definition or the table constructor arguments, or discovered in
the table metadata provided by **describeTable(), then the table cannot
be used with Zend_Db_Table.
Trying to force the primary key isn't working because it looks like your table doesn't have a column called id which you are specifying as the primary key.
The solution would be to add a primary key to the table you are trying to use.
In your model class that extends Zend_Db_Table_Abstract you can specify a primary key that isn't ID using protected $_primary = 'primary_column';