I've been developing a web app using symfony2 and now I added some Angularjs.
I have an input field, where you can filter products by name, the problem is the following, I've a controller in php, I do some queries, and then I render the view, passing the parameters, in this case I do something like this,
.......
return $this->render('default/index.html.twig',array( 'products' => $products));
My question is, if I wanted to filter those products by name using angular, how can I accomplish that? (I wanted something like phonecat-app in the official angular tutorial, where you can filter by name)
So far I've done this:
var StoreApp = angular.module('StoreApp', []);
StoreApp.controller('StoreCtrl', function($scope,$http){
$http.get('http://localhost:8000').success(function(data){
$scope.stores = data;
});
});
The problem is that I don't know which URL to put in the GET parameter, I've several tables in the database, and I don't know how to address them.
I'm using a local web server on port 8000, and Doctrine.
In order to filter the data on the client side using angular, you need to get the data from your symfony2 application into the angular scope with javascript.
There are multiple ways of doing this, the quick and dirty way is to render the products array from php directly in the angular ng-init attribute as explained in https://stackoverflow.com/a/28508012/1016372 .
In my opinion the best way to get the data into your angular application is creating a "RESTful endpoint" that exposes your product data in JSON format to your angular application. Using symfony2 you could create a controller that returns the product data if you make a query for http://localhost:8000/products with a controller similar to this snippet:
class ProductController
{
public function getProductsAction()
{
$products = $this->getRepository('Products')->findAll(),
return new JsonResponse($products);
}
}
Finally I could solve the problem.
Here is my controller,
public function getAllStoresAction()
{
$em = $this->getDoctrine()->getEntityManager();
$query = $em->createQuery('SELECT s FROM AppBundle\Entity\Store s');
$result = $query->getArrayResult();
return new Response(json_encode($result), 200);
}
then my app.js
var storeApp = angular.module('storeApp', []).config(function($interpolateProvider){
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
});
storeApp.controller('StoreCtrl',function($scope,$http){
$http.get('/get-all-stores').success(function(data){
$scope.stores = data;
});
$scope.orderProp = 'name';
});
and my routing.yml
get_all_stores:
path: /get-all-stores
defaults: { _controller: AppBundle:Default:getAllStores}
I managed to get the data back, but the problem was that it returned me an empty array.
Searching a bit, I found out that I wasn't serializing the data, php does not do that automatically, so I used this getArrayResult(); and it worked fine!
Thanks for the help Peter Peerdeman
Related
I am creating the API using slim framework. I faced the following problem.
I use one of the routes for given input.That is, json input: { "tagname": "tname"}. Route is
$app->post('/tag',function () use($app, $db){
//code
});
Now, I want to use the same route for another input.json: [{"tid": "1"},{"tid": "2"}]. Route is
$app->post('/tag',function () use($app, $db){
//code
});
How do solve it?
Slim's router can't call different functions for same path based on received content.
In your particular case the simplest way to deal with two different types of input data on one route would be something like this (I assume you are getting data as POST body with application/json which is not processed by Slim2)
$app->post('/tag',function () use($app, $db){
$payload = json_decode(file_get_contents('php://input'));
if(is_array($payload)) {
// code to deal with [{"tid": "1"},{"tid": "2"}]
} else {
// code to deal with { "tagname": "tname"}
}
});
But even easier and logically would be make /tag route for single and /tags for multiple. Or just require to send all tags as array - even single one.
you can pass extra parameter to perform another action in same route and separate your code with if condition
I am building a mobile app (iOS) and Symfony2 REST backend. On Symfony2, my routes are working correctly and I have tested them with AJAX and httpie, all CRUD operations, etc are fine. Now, I am trying to access the routes from the app. So far, I can access the routes and when I look into the Symfony2 Profiler, I can see entries in last 10 entries to verify that I am hitting the server with my POST and GET requests. Now, I have 2 questions and I would be glad if people can point me in the direction for ** Best Practices ** on how to proceed.
Problem 1: Although I am posting data which I can see coming in under "Request", when I try to create a record, it creates only NULL records, meaning the data is being lost. This is my controller for creating users for example:
public function postUserAction(Request $request)
{
$content = $this->get('request')->getContent();
$serializer = $this->get('jms_serializer');
$entity = $serializer->deserialize($content, 'Name\BundleName\Entity\User', 'json');
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return array(
'entity' => $entity,
);
}
When I look into the log, the only things that stand out are: Request Cookies (No cookies), Request Content: "Request content not available (it was retrieved as a resource)." This tells me the data was missing, how can I get this data and use it? Or what else could it be?
Problem 2: GET returns an empty JSON response with no data just the keys when I NSlog (echo it). My code looks like:
public function getUsersAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('NameBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
From the log, it has the Request Cookies set: PHPSESSID => "1udududjjs83883jdlb4ho0j4" but again the Request Content says: "Request content not available (it was retrieved as a resource)." How can I make it return the data with the JSON? This works well in the browser AJAX and httpie tests.
Problem 3: Using AFNetworking, I have a symbolic constant which I set as the APIHost (IP Address) and APIPath was the folder. Now in my earlier version using native PHP, I constructed the actual code to be executed in index.php by sending the parameter in JSON so if I wanted a login, I sent something like todo:login but with Symfony2, I am not sure or know even the best practices for this case. Ideally, I would like to specify the server-side request in the JSON request and then find the correct route in Symfony2 but is this how to do it and if yes, can you please provide an example? The workaround is to specify hard coded paths in AFNetworking each time I need to make a request which I think tightly couples the code and I need to make changes in a lot of places anytime something changes on the server side. Thanks and sorry for the long question!
You expect the jmsserializer to do magic for you. But it won't, you have to configure it first. From you code I can see that you are using jmsserializer wrong.
In getUsersAction() you have to return a serialized response, but you are returning an array of objects. This would be the right way:
public function getUsersAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('NameBundle:User')->findAll();
$serializer = $container->get('jms_serializer');
return array(
'users' => $jsonContent = $serializer->serialize($entities, 'json');,
);
}
Your post action basically looks ok, however when the json does not contain every field of entity USER the deserialization will fail. You can configure the entity for serialization/deserialization using annotations.
http://jmsyst.com/libs/serializer/master/reference/annotations
I am not sure if I understood your last problem, but I think you have to hardcode the path in your app.
Symfony2 is great and absolutely useful when writing an API. But if you don't want to deal with serialization/deserialization you can give http://laravel.com/ a try. It is build on symfony and you can generate an api on the fly.
Can someone point me in the proper direction for creating a zend-mvc style postDispatch plugin?
I tried using afterDispatchLoop() but I need a way to access the current response. The goal is to do some sort of filter on the Response (rendered layout + view) , and then reset the response with the new filtered text.
I also tried using afterRender() and beforeRender() but had no luck because the response that I get from the dependency injector is empty.
Please help if you know how to achieve this! Thank you!
UPDATE
I have got it working using the View event (afterRender). This event passes an instance of the Phalcon MVC view, where you can subsequently modify the body of the entire view but calling $view->getContent() + manipulation, followed by $view->setContent().
I am used to working with HTTP Response objects like in Zend. Is there a better way to achieve this sort of response manipulation in Phalcon?
if you get empty results from services in dependency injector, you can enable php singleton design pattern for services with just adding "true" flag like this:
$di->set('view', function() use($config) {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir($config->application->viewsDir);
$view->registerEngines(array(
".volt" => 'volt',
));
return $view;
}, true);
$di->set('service', function(){}, SINGLETON_TRUE_OR_FALSE)
more info here: http://docs.phalconphp.com/en/latest/reference/di.html#shared-services
My frontend is backbone and frontend is codeigniter with Phil Sturgeon’s REST Controller.
I have one model: Publisher
and one collection: Publishers
App.Collections.Publishers = Backbone.Collection.extend({
model: App.Models.Publisher,
url: '/restpublisher'
});
my RestPublisher controller has:
function index_post(){
$this->response('in pulisher_post');
}
function index_delete(){
$this->response('in pulisher_delete');
}
function index_put(){
$this->response('in pulisher_put');
}
The problem is that on this.model.save(); the url that is fired is: http://pubhub.local/restpublisher/1111 where 1111 is the id.
problem is that i get 404. if i just simulate a put request to http://pubhub.local/restpublisher/ everything works fine and i guess i can get the params from request->put()
is there a way to solve this problem?
question 2: can someone please explain me why the name of the action should start with index?
why can't i just write action: publishers_post that on save of the collection will get the data?
Thanks a Lot!
Roy
I'll start with the second question: I guess it's just easier to parse, and it'll just be boilerplate anyway, so why not index_?
Now, to go on with the interesting part. If your work with models inside a collection, Backbone, as a default behavior, will use the collection's URL to build a default one for your model (collection.url/model.id). However, you can override this by setting a value for your model's URL: url: '/restpublisher'.
Source
Edit:
To give you the general idea of how it works, it's easier to quote Backbone's Model#url code:
url: function() {
var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
}
So basically, if you override the url attribute in your model, this function will be erased, and the behavior you don't want as well.
Experience level: newbie.
The backbone.js Todos demo uses localStorage. This question is about how to use PHP to serve the page instead, assuming that a MySQL DB has been set up.
I checked out PHP frameworks such as CodeIgniter but found them difficult to follow and possibly overkill for my learning purposes.
I understand the concept that a REST API needs to be set up. I am really looking for simple code samples. Thanks in advance.
Update: Is there a full backbone.js tutorial somewhere that includes a full working example of how to wire up to server side PHP?
The most basic and simple approach (I know of) that should help you to get started, would be:
Given you have a model / collection, define it with an url like:
resourceCollection: Backbone.Collection.extend({
url: '/page.php'
})
Create your page.php file (in the document root), just take care of RewriteRules etc. you may use!
Now we have to make sure that we can react properly on get, put, post and delete; so we have to check for the request method, e.g. with a switch statement. Cases would be GET, DELETE, PUT, POST:
switch($_SERVER['REQUEST_METHOD']){
...
}
The following should give you an idea (php controllers are implemented using Silex framework + Paris library for the data access):
// GET /{resource}/{id} Show
$app->get('/api/todos/{id}', function ($id) use ($app) {
$todo = $app['paris']->getModel('Todo')->find_one($id);
return new Response(json_encode($todo->as_array()), 200, array('Content-Type' => 'application/json'));
});
// POST /{resource} Create
$app->post('/api/todos', function (Request $request) use ($app) {
$data = json_decode($request->getContent());
$todo = $app['paris']->getModel('Todo')->create();
$todo->title = $data->title;
$todo->save();
return new Response(json_encode($todo->as_array()), 200, array('Content-Type' => 'application/json'));
});
// PUT /{resource}/{id} Update
$app->put('/api/todos/{id}', function ($id, Request $request) use ($app) {
$data = json_decode($request->getContent());
$todo = $app['paris']->getModel('Todo')->find_one($id);
$todo->title = $data->title;
$todo->save();
return new Response('Todo updated', 200);
});
// DELETE /{resource}/{id} Destroy
$app->delete('/api/todos/{id}', function ($id) use ($app) {
$todo = $app['paris']->getModel('Todo')->find_one($id);
$todo->delete();
return new Response('Todo deleted', 200);
});
To get your backbone collection working with the above interface, all you have to do is to set the url property like:
window.TodoList = Backbone.Collection.extend({
model: Todo,
url: "api/todos",
...
});
Recently, I have written a tutorial on how to do GET/POST/PUT/DELETE with Backbone.js and PHP http://cambridgesoftware.co.uk/blog/item/59-backbonejs-%20-php-with-silex-microframework-%20-mysql, might be helpful.
The example:
https://github.com/ccoenraets/wine-cellar-php
... from this article:
http://coenraets.org/blog/2011/12/restful-services-with-jquery-php-and-the-slim-framework/
... is good because it encapsulates a configured PHP RESTful server (Slim). From the perspective of working with backbone.js, this seems to essentially be the entirety of what you need on the server side - simply a RESTful service!
For my basic web config (am not good with rewrite rules), I had to modify the file ../final/js/models/winemodel.js (where I add index.php) as follows:
url:"../api/index.php/wines"