i am writing webservice in symfony2 but i facing some problem regarding the output ,as it is giving blank output.
class DefaultController extends Controller {
/**
*
* #Route("/webservices/activity/{id}", name="user_json_activity")
* #Method("get")
*/
public function activityAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$list = $em->getRepository('FitugowebserviceBundle:activity')->findOneById($id);
$r_array = $this->routes2Array($list);
$r = array('activity' => $r_array);
return new Response(json_encode($r));
}
private function routes2Array($routes) {
$points_array = array();
foreach ($routes as $route) {
$r_array = array('activity' => $route->getActivity(),
'icon' => $route->getIcon());
$points_array[] = $r_array;
}
return $points_array;
}
}
When i try to fetch data for id=1 http://domain.org/fitugo/web/app_dev.php/webservices/activity/1 it is giving output as follows
{"activity":[]}
It look very strange that you want get array with findOneById method. The first thing I suggest to add a check that the entity founded by id exist. Then look that findOneById returns and check your controller logic.
Related
I have an entity, call it Stones and Stones has a ManyToMany relationship with Attributes.
So I query the entity to get the Stones and then I hydrate this to convert it into an array.
$result = $this->stoneRepository->find($stone_id);
if ( ! $result )
{
return false;
}
$resultArray = $this->doctrineHydrator->extract($result);
This works fine for the Stone entity however I noticed that the join (Attributes) remain as objects.
array (size=12)
'id' => int 1
'name' => string 'Agate' (length=5)
'title' => string 'Title' (length=5)
'attribute' =>
array (size=5)
0 =>
object(Stone\Entity\StAttribute)[1935]
private 'id' => int 2
private 'name' => string 'Hay fevor' (length=9)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
1 =>
object(Stone\Entity\StAttribute)[1936]
private 'id' => int 15
private 'name' => string 'Libra' (length=5)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
2 =>
etc.
What is the process to hydrate the Attribute objects?
Hydration is populating an object (entity) using an array which is opposite of the extraction.
Since you want the resultset in array format, you should prevent unnecessary hydration and extraction process which already occurs in the ORM level under the hood.
Try to use Query Builder Api instead of built-in find() method of the entity repository. This is not a single-line but really straightforward and faster solution, it should work:
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A')
->leftJoin('S.attribute', 'A')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
This way, you will also prevent running additional SQL queries against database to fetch associated entities. (StAttribute in your case)
I thought I would follow up on this to show how this can be resolved using a CustomStrategy.
By far the easiest and fastest method was suggested by foozy. What I like about the solution is that when I use hydration in ApiGility for instance I can build custom queries which will produce the desired result in a very few lines of code.
The other solution I was working on was to add a custom strategy:
<?php
namespace Api\V1\Rest\Stone;
use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;
class CustomStrategy extends AbstractCollectionStrategy
{
public function __construct($hydrator)
{
$this->hydrator = $hydrator;
}
/**
* #param mixed $values
* #return array|mixed
*/
public function extract($values)
{
$returnArray = [];
foreach ($values AS $value)
{
$returnArray[] = $this->hydrator->extract($value);
}
return $returnArray;
}
/**
* #param mixed $values
* #return mixed
*/
public function hydrate($values)
{
$returnArray = [];
foreach ($values AS $value )
{
$returnArray[] = $this->hydrator->hydrate($value);
}
return $returnArray;
}
}
Then from the service side I add various strategies to the hydrator like so:
$result = $this->stoneRepository->find($stone_id);
$this->doctrineHydrator->addStrategy("product", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("attribute", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("image", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("related", new CustomStrategy( $this->doctrineHydrator ) );
$resultArray = $this->doctrineHydrator->extract($result);
After which I created a custom entity:
<?php
namespace Api\V1\Rest\Stone;
class StoneEntity
{
public $id;
public $name;
public $description;
public $code;
public $attribute;
public $product;
public $image;
public function getArrayCopy()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'code' => $this->code,
'attribute' => $this->attribute,
'product' => $this->product,
'image' => $this->image
);
}
public function exchangeArray(array $array)
{
$this->id = $array['id'];
$this->name = $array['name'];
$this->description = $array['description'];
$this->code = $array['code'];
$this->attribute = $array['attribute'];
$this->product = $array['product'];
$this->image = $array['image'];
}
}
And the final part is to exchange the returned data with the custom entity:
$entity = new StoneEntity();
$entity->exchangeArray($resultArray);
And finally to return the result:
return $entity;
To be honest, the above is just too long winded and my final solution as per the suggestion by foozy was this:
public function fetchOne($stone_id)
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->innerJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
if ( ! $resultArray )
{
return false;
}
return $resultArray;
}
I am trying to implement Tags to my posts. the user will input the tags in a textbox separated by commas.
public function addAction() {
$entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
$article = new Article;
$form = new ArticleForm();
$form->setHydrator(new DoctrineHydrator($entityManager,'CsnCms\Entity\Article'));
$form->bind($article);
$request = $this->getRequest();
if ($request->isPost()) {
$post = $request->getPost();
$form->setData($post);
if ($form->isValid()) {
$this->createTags($article, $post["Tags"]);
$this->prepareData($article);
$entityManager->persist($article);
$entityManager->flush();
return $this->redirect()->toRoute('csn-cms/default', array('controller' => 'article', 'action' => 'index'));
}
}
return new ViewModel(array('form' => $form));
}
in the above code i have added a class called createTags that i am planning to split the inputted tags into an array and create a new tag entity for each and then store the new tag entities in a array in the article object. Is this the correct way I should be doing this?
No, you wont be able search your pages based on tag if you store them in an array. you need to have a separated db table with columns like id,tag,pageId so you can properly search the pages with tag names.
Sorry if the question want that detailed was just wondering if I should use a filter instead (I think this would work but dont know if it is good practice)
In the end I just use the processData function
public function prepareData($article, $post) {
$separator = ",";
if($post['tagsString'] != "")
{
//Link Tags
$array = array_unique(explode($separator, $post['tagsString']));
foreach ($array as $tagString) {
$tag = $this->getEntityManager()->getRepository('Cms\Entity\Tag')->findOneBy(array('tag' => $tagString));
$link = new \Cms\Entity\LinkTagToArticle($article, $tagString, $tag);
$this->getEntityManager()->persist($link);
}
}
}
This question is one in a series I seem to be generating as I slowly pick my way through learning testing.
I have a book model and a ticketaudit model. They have a relationship one to many. When a book is created a function should also create a range of tickets (for audit).
I want my test to make sure the ticketAudit model is being created and the association being made using the eloquent ORM within laravel.
my class so far:
Class TicketCreator implements TicketCreatorInterface {
protected $ticket;
public function __construct(TicketAudit $ticketAudit)
{
//dd($ticketAudit);
$this->ticket = $ticketAudit;
}
public function createTicket($input, $book) {
$counter = $input['start'];
while($counter <= $input['end']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
}
and my attempts at a test:
public function testCreateCreatesTickets() {
//arrange
$book = FactoryMuff::create('Book');
$aTicket = FactoryMuff::create('TicketAudit');
$ticketAudit = new TicketAudit;
$ticketCreator = new TicketCreator($ticketAudit);
//act
$response = $ticketCreator->createTicket(array('start'=>1000, 'end'=>1001), $book);
// Assert...
$this->assertEquals(true, $response);
}
However when I run this test I get the error:
Integrity constraint violation: 19 ticket_audits.ticketnumber may not be NULL
For some reason the model is not being created with the values I pass to it. I've checked in the function that the object exists and also the values are being created correctly in the array but it doesnt work.
Is this unique to testing?
I am creating an sqlite in memory database for this test.
Any help appreciated
Crikey this decision to start testing is a bit of a nightmare
Thanks to Manuel's request to post the TicketAudit class I noticed my model extended Eloquent. I had recently added Ardent and should have extended Ardent so the error lay in the model!
Revised corrected model :
use LaravelBook\Ardent\Ardent;
class TicketAudit extends Ardent {
protected $guarded = array();
public $autoHydrateEntityFromInput = true;
public $autoPurgeRedundantAttributes = true;
public static $rules = array(
'status' => 'required',
'ticketnumber' => 'required'
);
public static $factory = array(
'ticketnumber' => '1000',
'status' => 'unused',
);
public function book() {
return $this->belongsTo('Book');
}
}
Thank you for the sign post
I'm going to use Prestashop 1.5.4 web-service to get all products with their attributes such as description, name and etc. My problem is whenever I call web-service it returns to me only the products Ids. How can I get attributes too?
Edited :
code :
class ShopApi
{
public $client;
public function __construct()
{
$this->getClient();
}
public function getClient()
{
try {
// creating web service access
$this->client = new PrestaShopWebservice('http://wikibazaar.ir/', 'A38L095W0RHRXE8PM9CM01CZW7KIU4PX', false);
} catch (PrestaShopWebserviceException $ex) {
// Shows a message related to the error
echo 'error: <br />' . $ex->getMessage();
}
}
}
class ProductApi extends ShopApi
{
public function findAll()
{
$products = array();
/// The key-value array
$opt['resource'] = 'products';
$opt['display'] = '[description]';
$opt['limit'] = 1;
$xml = $this->client->get($opt);
$resources = $xml->products->children();
foreach ($resources as $resource)
$products[] = $resource->attributes();
return $products;
}
}
EDIT :
I've found that the response from webservice is ok. but there is a problem during parsing xml with simplexml_load_string() function. any idea?
it's $product var_dump :
SimpleXMLElement#1 ( [products] => SimpleXMLElement#2 ( [product] => SimpleXMLElement#3 ( [description] => SimpleXMLElement#4 ( [language] => SimpleXMLElement#5 ( [#attributes] => array ( 'id' => '1' ) ) ) ) ) )
I think that $opt['display'] = 'full'; would do the job
You can also select only some specific attribute e.g.
$opt['display'] = '[id,name]';
Take a look at the official documentation, you might find it interesting
I have been getting into Unit Testing with Zend Framework. I am getting used to the other things it provide but I am having a hard time understanding Mock Objects.
For this example, I am trying to use a Mock Object to test out my model.
<?php
class Twitter_Model_Twitter
{
private $_twitter;
/**
* Make the options injectable.
* __contruct($auth, $key)
*/
public function __construct()
{
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $password, MCRYPT_MODE_ECB, $iv);
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
}
For my unit test:
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
/**
* #var Model_Twitter
*/
protected $_twitter;
public function testfriendsTimeline()
{
$mockPosts = array('foo', 'bar');
//my understanding below is:
//get a mock of Zend_Service_Twitter with the friendsTimeline method
$twitterMock = $this->getMock('Zend_Service_Twitter', array('friendsTimeline'));
/*
line above will spit out an error:
1) testfriendsTimeline(Model_TwitterTest)
Missing argument 1 for Mock_Zend_Service_Twitter_9fe2aeaa::__construct(), called in
/Applications/MAMP/bin/php5/lib/php/PHPUnit/Framework/TestCase.php on line 672 and
defined /htdocs/twitter/tests/application/models/TwitterTest.php:38
*/
$twitterMock->expects($this->once())
->method('friendsTimeline')
->will($this->returnValue($mockPosts));
$model = new Twitter_Model_Twitter();
$model->setOption('twitter', $twitterMock);
$posts = $model->friendsTimeline(array('count'=>20));
$this->assertEquals($posts, $mockPosts);
}
}
How would you test the following?
1) verifyCredentials()
2) friendsTimeline()
Thanks,
Wenbert
I am going to answer this question. I think I have made this work thanks to zomg from #zftalk.
Here is my new Twitter Model:
<?php
//application/models/Twitter.php
class Twitter_Model_Twitter
{
private $_twitter;
private $_username;
private $_password;
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
$this->_twitter = new Zend_Service_Twitter($this->_username, $this->_password);
} else {
$twitterAuth = new Zend_Session_Namespace('Twitter_Auth');
$config = new Zend_Config_Ini(APPLICATION_INI, APPLICATION_ENV);
$key = $config->encryption->salt;
$iv_size = mcrypt_get_iv_size(MCRYPT_XTEA, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$password = mcrypt_decrypt(MCRYPT_XTEA, $key, $twitterAuth->password, MCRYPT_MODE_ECB, $iv);
$username = $twitterAuth->username;
$this->_twitter = new Zend_Service_Twitter($username, $password);
}
}
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$pieces = explode('_', $key);
foreach($pieces AS $piece_key => $piece_value) {
$pieces[$piece_key] = ucfirst($piece_value);
}
$name = implode('',$pieces);
$method = 'set' . $name;
//$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
//I added this method. So that I could "inject"/set the $_twitter obj
public function setTwitter($obj)
{
$this->_twitter = $obj;
return $this;
}
public function verifyCredentials()
{
return $this->_twitter->account->verifyCredentials();
}
public function friendsTimeline($params)
{
return $this->_twitter->status->friendsTimeline($params);
}
//in the real code, more will go here...
}
And in my Unit Test, I have this:
<?php
// tests/application/models/TwitterTest.php
require_once ('../application/models/Twitter.php');
class Model_TwitterTest extends ControllerTestCase
{
public function testVerifyCredentials()
{
$stub = $this->getMock('Zend_Service_Twitter', array('verifyCredentials'),array(),'',FALSE);
//FALSE is actually the 5th parameter to flag getMock not to call the main class. See Docs for this.
//Now that I have set the $_twitter variable to use the mock, it will not call the main class - Zend_Rest_Client (i think)
$stub->expects($this->once())
->method('verifyCredentials');
$model = new Twitter_Model_Twitter();
//this is the part where i set the $_twitter variable in my model to use the $stub
$model->setOptions(array('twitter'=>$stub));
$model->verifyCredentials();
}
}
Anyways, I think I got it working.
1) The unit test no longer tried to connect to twitter.com:80
2) After I got the setOptions() working in the Twitter_Model, $model->verifyCredentials() in my unit test was successfully called.
I will wait for others in Stackoverflow to confirm that is the right answer. For the meantime, would like to hear from you guys.
Thanks!!!