Symfony (a bit more dynamic ?) routing - symfony

I am new to symfony. As an exersice I`m trying to make some basic cms.And I was wondering is this aproach of routing wrong:
/**
* #Route("/back-office/", name="back-office")
*/
public function indexAction(Request $request,$page="")
{
switch($page){
case "":
return $this->render('CmsBundle:BackOffice:index.html.twig');
break;
default:
return $this->render('CmsBundle:BackOffice:site-map.html.twig');
break;
}
}
This is my yaml confing:
back_office_pages:
path: /{page}
defaults: { _controller: CmsBundle:BackOffice:index}
By using this aproach I wont have to configure each route in the yaml file. Since routes may vary. But I am not quite sure this is the symfony way of doing things so I decided to ask for advice..
What I`m trying to achive:
Lets say we have a user that have less back-end programing expirience or not at all and he stumbuled upon the CMS. The goal is to add front end pages using some user interface. Then we store the pages(slug) in the database. In the index action we retrive this data. From the database we can also assing template to a page (we need the user to have at least some html+css+twig).
So what we do is get the pages that user added :
ex : Gallery, Contacts
we check the request url
and if the page requested is in the array from the database we return the template related to the page.
NOTE:
If you disagree with this method please do not bash me but eplain why is this wrong. Because as I said I am still new with the framework.

Try setting your routing to:
back_office_pages:
resource: "#CmsBundle/Controller/"
type: annotation
to set up Routing Annotations inside your CmsBundle.
Then, your action should be working using the url "/back-office/{page}"

Related

Redirecting in symfony

I'm trying to understand if there is any difference between these two ways to do a redirection in symfony.
1 Via config without custom controller
As explained here.
# app/config/routing.yml
# ...
codes:
path: /codes # redirect /codes to /code
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /code
permanent: true
2 Via redirect() method in a custom controller
As explained here.
class RedirectsController extends Controller {
/**
* #Route("/codes")
*/
public function codesAction() {
return $this->redirect('/code', 301); // redirect /codes to /code
}
}
When I talk about differences I mean things like performance, ease of use and maintainability.
Thanks.
In the first case you don't need to create a controller. You can manage everything from the configuration, and this is a convenience.
The redirect will be made by RedirectController. In terms of performance there is no difference between the two methods.
If what you have to do is redirect then go with the first method.
The thing is your first solution will redirect without using a specific controller, using only configuration, whereas your second solution will do your action and then redirect. These are two different use cases and they are not the same.

BreezeJS modified route not working

My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.

Symfony calling functions between controllers

Can anyone give me direction on how to accomplish cross controller variable exchange and/or function calls?
I'm new to Symfony and I have a reasonably complex practice sample site which has two controllers - PageController and BlogController.
PageController has actions to generate my home, about and contact page. The home page simply has a list of blogs.
The BlogController has all the CRUD related functions - create, delete etc
My issue is that I want to call my BlogController:createAction function from the PageController so I can present a blog create form above the blog listings on the homepage OR just pass the variable containing the new blog form data.
In addition, I need to find a solution which will allow the form to submit and the listings to refresh via AJAX.
Although #Tom Toms answer is correct, I would recommend another approach in order to minimize dependencies:
Just create a route for your Blog function and then use $this->redirectToRoute in your PageController thereby the action will be simply redirected to the assigned route, so you have no forwarding (although I think that the forwad action will create nothing more then a redirect) or service implementation with injections just a simple redirect
Either using forward method directy
$response = $this->forward('AcmeDemoBundle:Blog:myFunction', array(
'name' => $name,
));
http://symfony.com/doc/current/book/controller.html#forwarding-to-another-controller
Or alternatively, you could define the blog controller as a service.
service.yml
services:
your_service:
class: Acme\DemoBundle\Controller\BlogController
Then you can use
$service = $this->get('your_service');
$service->myFunction();
You could also look into setting up a form factory service (bit more advanced)
UPDATE:
Following your post I implemented the a service but got the following error:
Error: Call to a member function get() on a non-object
This got fixed when I added the following line to the service declaration:
calls:
- [ setContainer, [ #service_container ]]

How to approach making a controller with (unlimited) db look-up routes

I'm digging into ASP.NET MVC from classic asp, and have completed some tutorials. I understand the concept now, but I have a main question about the controller. How are you able to control the url structure if you are getting your url's (with params) from a sql database?
Example: /custom-url-1 or /custom-url-23423411
(Returns params accordingly to feed the code)
I'm guessing it would have to do with ActionResult Index() , but not sure where to go after that. Any idea's where to look or is this even possible? Does MVC even allow this?
One Way you can approach this is to have everything go to one action in one controller and resolve the content in the view.
This is useful only if you have one type of view.
Second way is to have route constraint or a custom route constraint for each type of content you have
say : Galleries, Blog, Pages
and in each constraint check if the given url is of this type ( by db call), if the constraint returns true, it will point the request to the given controller and action.
The third way is to have a custom route handler that does the checking and routing (mind you this is probably the hardest task but works best if you have complex system, if your is simple try using method 1 or 2
P.S. if you want your urls separataed by "-" instead of "/" you can do just this
routes.MapRoute(
"Default", // Route name
"{controller}-{action}-{id}",// URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

Symfony2 output any HTML controller as JSON

I have a website completed that was created in Symfony2 and I now want a lot of the features of the site to now be made available in a mobile app.
My idea is by appending a simple URL variable then it will output all the variables of the relevant page request in JSON.
So if I connect to
www.domain.com/profile/john-smith
It returns the HTML page as now.
But if I go to
www.domain.com/profile/john-smith?app
Then it returns a JSON object of name, age and other profile info.
My app code then receives the JSON and processes.
I can't see any security issues as it's just really the variables presented in JSON and no HTML.
By doing the above I can create all the app code and simply make calls to the same URL as a web page, which would return the variables in JSON and save the need for any more server-side work.
The question is: How would I do this without modifying every controller?
I can't imagine an event listener would do it? Maybe I could intercept the Response object and strip out all the HTML?
Any ideas as to the best-practice way to do this? It should be pretty easy to code, but I'm trying to get my head around the design of it.
There is a correct way to configure the routes for this task
article_show:
path: /articles/{culture}/{year}/{title}.{_format}
defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
requirements:
culture: en|fr
_format: html|rss
year: \d+
However, this would still require you to edit every Controller with additional control structures to handle that output.
To solve that problem, you can do two things.
Create json templates for each template you have, then replace html in template.html.twig with template.'.$format.'.twig. (Be careful to ensure users can't pass a parameter without validation in the url, this would be a major security risk).
Create your own abstract controller class and override the render method to check the requested format and provide output based on that.
class MyAbstractController extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
if($this->getRequest()->getRequestFormat() == 'json')
{
return new Response(json_encode($parameters));
}
else
{
parent::render($view, $parameters, $response);
}
}
}
NOTE The above code is a prototype, don't expect it to work out of the box.
I personally would deem the second method more correct, because there is no duplication of code, and less security concern.

Resources