Here i was i have :
A RESTful Symfony API that i created with few bundles
A front AngularJS that i have in the web repository
Now here is a sample of my routing file :
$routeProvider.
when('/liste-produits', {
templateUrl: '../templates/list-products.html',
controller: 'ProductListCtrl'
}).
otherwise({
redirectTo: '/'
});
The fact that i have to use "../". Because otherwise it won't work in dev environnement (app_dev.php). And of course by the time i will post it in production (app.php) i won't need to add this "../"
Do you guys understand my problem ?
Since i can get assetic from Symfony work in the routing file.
How can i solve this ?
There is an approach, where you define a global variable in your base twig file:
Symfony 2:image paths in javascript file with assetic which you can in turn use in e.g. AngularJS.
There is also a bundle called FOSJsRoutingBundle, it sort of exposes your routes to the client and thus javascript. That might be interesting for you.
However there is another option; - I have personally used the approach posted by Rein Baarsma with the twig file and then cached the resulting javascript.
It's fairly simple to write a request listener that renders the twig file to a javascript file once a day or whenever the javascript file is deleted.
I used the same approach with the stylesheets for a project with daily changing colors.
If you do not cache it, the browser will revisit the route returning the javascript on each page and rerender the javascript file, which adds a lot of overhead.
You could simply make a Symfony Controller with a view on a js file. That way you can use the twig (or php) template functions (like twig's path() function) to avoid any strange urls.
In your controller:
public function routingAction(Request $request) {
$this->render('angular/routing.twig.js');
}
And in your routing
$routeProvider.
when('/liste-produits', {
templateUrl: {{ path('product_list') }},
controller: 'ProductListCtrl'
}).
otherwise({
redirectTo: '/'
});
Related
I am working on a newly created Phalcon project, and I don't really know how to actually use multiples views.
What is the entry point? I don't really know when each method in the controller is called, under which conditions, etc.
Where is the control flow defined? is it based in the name of the view? or is there a place where you can register them?
Phalcon is a bit different than other well-known PHP frameworks, in that not much is pre-configured or pre-built by default. It's quite loosely-coupled. So you have to decide where and how your control flow will work. This means that you will need to dig deeper in the documentation and also that there could be different way to achieve the same thing.
I'm going to walk you through a simple example and provide references, so you can understand it more.
1) You would start by defining a bootstrap file (or files) that will define the routes, or entry points, and will setup and create the application. This bootstrap file could be called by an index.php file that is the default file served by the web server. Here is an example of how such bootstrap file will define the routes or entry points (note: these are just fragments and do not represent all the things that a bootstrap file should do):
use Phalcon\Di\FactoryDefault;
// initializes the dependency injector of Phalcon framework
$injector = new FactoryDefault();
// defines the routes
$injector->setShared('router', function () {
return require_once('some/path/routes.php');
});
Then it the routes.php file:
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Router\Group as RouterGroup;
// instantiates the router
$router = new Router(false);
// defines routes for the 'users' controller
$user_routes = new RouterGroup(['controller' => 'users']);
$user_routes->setPrefix('/users');
$user_routes->addGet('/show/{id:[0-9]{1,9}}', ['action' => 'show']);
$router->mount($user_routes);
return $router;
Im defining routes in an alternate way, by defining routes groups. I find it to be more easy to organize routes by resource or controller.
2) When you enter the url example.com/users/show/123, the routes above will match this to the controller users and action show. This is specified by the chunks of code ['controller' => 'users'], setPrefix('/users') and '/show/{id:[0-9]{1,9}}', ['action' => 'show']
3) So now you create the controller. You create a file in, let's say, controllers/UsersController.php. And then you create its action; note the name that you used in the route (show) and the suffix of Action:
public function showAction(int $id) {
// ... do all you need to do...
// fetch data
$user = UserModel::findFirst(blah blah);
// pass data to view
$this->view->setVar('user', $user);
// Phalcon automatically calls the view; from the manual:
/*
Phalcon automatically passes the execution to the view component as soon as a particular
controller has completed its cycle. The view component will look in the views folder for
a folder named as the same name of the last controller executed and then for a file named
as the last action executed.
*/
// but in case you would need to specify a different one
$this->view->render('users', 'another_view');
}
There is much more stuff related to views; consult the manual.
Note that you will need to register such controller in the bootstrap file like (Im also including examples on how to register other things):
use Phalcon\Loader;
// registers namespaces and other classes
$loader = new Loader();
$loader->registerNamespaces([
'MyNameSpace\Controllers' => 'path/controllers/',
'MyNameSpace\Models' => 'path/models/',
'MyNameSpace\Views' => 'path/views/'
]);
$loader->register();
4) You will also need to register a few things for the views. In the bootstrap file
use Phalcon\Mvc\View;
$injector->setShared('view', function () {
$view = new View();
$view->setViewsDir('path/views/');
return $view;
});
And this, together with other things you will need to do, particularly in the bootstrap process, will get you started in sending requests to the controller and action/view defined in the routes.
Those were basic examples. There is much more that you will need to learn, because I only gave you a few pieces to get you started. So here are some links that can explain more. Remember, there are several different ways to achieve the same thing in Phalcon.
Bootstrapping:
https://docs.phalconphp.com/en/3.2/di
https://docs.phalconphp.com/en/3.2/loader
https://docs.phalconphp.com/en/3.2/dispatcher
Routing: https://docs.phalconphp.com/en/3.2/routing
Controllers: https://docs.phalconphp.com/en/3.2/controllers
More on Views (from registering to passing data to them, to templating and more): https://docs.phalconphp.com/en/3.2/views
And a simple tutorial to teach you some basic things: https://docs.phalconphp.com/en/3.2/tutorial-rest
The application begins with the routing stage. From there you grab the controller and action from the router, and feed it to the dispatcher. You set the view then call the execute the dispatcher so it access your controller's action. From there you create a new response object and set its contents equal to the view requests, and finally send the response to the client's browser -- both the content and the headers. It's a good idea to do this through Phalcon rather than echoing directly or using PHP's header(), so it's only done at the moment you call $response->send(); This is best practice because it allows you to create tests, such as in phpunit, so you can test for the existence of headers, or content, while moving off to the next response and header without actually sending anything so you can test stuff. Same idea with exit; in code, is best to avoid so you can write tests and move on to the next test without your tests aborting on the first test due to the existence of exit.
As far as how the Phalcon application works, and in what steps, it's much easier to follow the flow by looking at manual bootstrapping:
https://docs.phalconphp.com/en/3.2/application#manual-bootstrapping
At the heart of Phalcon is the DI, the Dependency Injection container. This allows you to create services, and store them on the DI so services can access each other. You can create your own services and store them under your own name on the DI, there's nothing special about the names used. However depending on the areas of Phalcon you used, certain services on the DI are assumed like "db" for interacting with your database. Note services can be set as either shared or not shared on the DI. Shared means it implements singleton and keeps the object alive for all calls afterwards. If you use getShared, it does a similar thing even if it wasn't initially a shared service. The getShared method is considered bad practice and the Phalcon team is talking about removing the method in future Phalcon versions. Please rely on setShared instead.
Regarding multiple views, you can start with $this->view->disable(); from within the controller. This allows you to disable a view so you don't get any content generated to begin with from within a controller so you can follow how views work from within controllers.
Phalcon assumes every controller has a matching view under /someController/someView followed by whatever extension you registered on the view, which defaults to .volt but can also be set to use .phtml or .php.
These two correspond to:
Phalcon\Mvc\View\Engine\Php and Phalcon\Mvc\View\Engine\Volt
Note that you DON'T specify the extension when looking for a template to render, Phalcon adds this for you
Phalcon also uses a root view template index.volt, if it exists, for all interactions with the view so you can use things like the same doctype for all responses, making your life easier.
Phalcon also offers you partials, so from within a view you can render a partial like breadcrumbs, or a header or footer which you'd otherwise be copy-pasting into each template. This allows you to manage all pages from the same template so you're not repeating yourself.
As far as which view class you use within Phalcon, there's two main choices:
Phalcon\Mvc\View and Phalcon\Mvc\View\Simple
While similar, Phalcon\Mvc\View gives you a multiple level hierarchy as described before with a main template, and a controller-action based template as well as some other fancy features. As far as Phalcon\Mvc\View\Simple, it's much more lightweight and is a single level.
You should be familiar with hierarchical rendering:
https://docs.phalconphp.com/en/3.2/views#hierarchical-rendering
The idea is with Phalcon\Mvc\View that you have a Main Layout (if this template exists) usually stored in /views/index.volt, which is used on every page so you can toss in your doctypes, the title (which you would set with a variable the view passed in), etc. You'd have a Controller Layout, which would be stored under /views/layouts.myController.volt and used for every action within a controller (if this template exists), finally you'd have the Action Layout which is used for the specific action of the controller in /views/myController/myAction.volt.
There are all types of ways you can break from Phalcon's default behavior. You can do the earlier stated $this->view->disable(); so you can do everything manually yourself so Phalcon doesn't assume anything about the view template. You can also use ->pick to pick which template to use if it's going to be different than the controller and action it's ran in.
You can also return a response object from within a controller and Phalcon will not try to render the templates and use the response object instead.
For example you might want to do:
return $this->response->redirect('index/index');
This would redirect the user's browser to said page. You could also do a forward instead which would be used internally within Phalcon to access a different controller and/or action.
You can config the directory the views are stored with setViewsDir. You can also do this from within the controller itself, or even within the view as late as you want, if you have some exceptions due to a goofy directory structure.
You can do things like use $this->view->setTemplateBefore('common') or $this->view->setTemplateAfter('common'); so you can have intermediate templates.
At the heart of the view hierarchy is <?php echo $this->getContent(); ?> or {{ content() }} if you're using Volt. Even if you're using Volt, it gets parsed by Phalcon and generates the PHP version with $this->getContent(), storing it in your /cache/ directory, before it is executed.
The idea with "template before" is that it's optional if you need another layer of hierarchy between your main template and your controller template. Same idea with "template after" etc. I would advise against using template before and after as they are confusing and partials are better suited for the task.
It all depends on how you want to organize your application structure.
Note you can also swap between your main template to another main template if you need to swap anything major. You could also just toss in an "if" statement into your main template to decide what to do based on some condition, etc.
With all that said, you should be able to read the documentation and make better sense of how to utilize it:
https://docs.phalconphp.com/en/3.2/api/Phalcon_Mvc_View
I have a view helper that needs jquery, jqueryui, knockout and few other js files to work. Some pages already references all the js files needed by view helper, some pages don't.
Right now, I am using the url to figure out if a js file should be referenced or not inside the view helper.
Is there a better way to do this?
Thanks.
I'm not 100% sure I understand your question in the right way, but I'm 100% sure you need to manage your javascript dependencies in a better way.
I usually use require.js module loader to manage javascript files included into a template.
In order to use it, you need to download require.js file and place it somewhere in your project. I believe you would like to place it in ./public/js/vendors directory not to keep it alongside with your own code. Also, you need to create config file for it, where you decide what javascript modules are needed.
So, the concept is pretty clear: if you have javascript files, that are related to some part of your project, for example admin panel, you should create a config that will load those files. I usually have one config for admin panel part of my web application, and another one for user side of it.
This config with require.js itself are included into template like this:
<script data-main="./js/config-admin.js" src="./js/vendor/require.js"></script>
You need to keep in mind, that on every template you should have only one inclusion of javascript - the inclusion of require.js and it's config.
The config usually looks like this:
require.config({
baseUrl: '/public/js',
paths: {
'bootstrap': './vendor/bootstrap',
'jquery': './vendor/jquery'
},
shim: {
'bootstrap': ['jquery']
}
});
require(['./app'], function (app) {
console.log(app); //Here is smth that required module 'public/js/app.js' returns
//Do some coding here if you wish, for example to start js application.
});
If you need more examples of usage, you can visit my github profile, there are a couple of repositories where I use it (symfony and zend2 applications).
If you are not managing your assets via other means, the HeadScript view helper is designed to act as a collection of the scripts that should be rendered in the view.
Considering the helper depends on the JS files, placing these in the helper's factory class would be a logical place.
class MyViewHelperFactory
{
public function __invoke(ViewPluginManager $viewHelperManager, $name, $requestedName)
{
$headScriptHelper = $viewHelperManager->get('HeadScript');
$headScriptHelper->appendFile('/js/file.js');
return new MyViewHelper();
}
}
There are of course better ways of doing these things; personally I like Assetic.
I have a PHP Slim and Backbone.JS setup and all my code is now working without any problems.
The only issue I have is that the code I have is minified into one file with Grunt.JS and is loaded at the bottom of each page.
So my Backbone render call is fired on all my pages within my site and not just the path I want it to run on.
I have now tried to use Backbones Router to fire the render on the path I want it to run on, I did not think this would work and it did not as I am using PHP slim as the routing agent and of course Backbone needs a /#/ route path.
Now when I had this Backbone route set up I did try to get PHP Slim to redirect the /#/ route to the clean PHP Slim route path. PHP Slim did not like this at all, when I use the following code,
$app->get('/#/MYPATHHERE', function () use ($app) {
$app->redirect('/REALLPATHTOGOTO');
});
it gave me a PHP Slim error, it looks like PHP Slim does not like the /#/ route.
So what is the best method for doing this?
I am thinking that I could just call the render function within the PHP page that I am getting PHP Slim to render on my selected route? or is there a better method for doing this?
Thanks
Glenn.
Ok got this to work with Backbone, did some more research and enabling pushState to true on the Backbone.history.start then it works without the need for the hash routing.
I might be approaching this the wrong way, but in my Base Controller I have defined, in the construct, my Asset container "header", eg:
Asset::container('header')->add('main-css', 'css/style.css');
// etc
As all my other controllers extend the base controller this is working fine and my assests are placed in the header container when I call
{{Asset::container('header')->styles()}}
In my master.blade.php
However, when a 404 is triggered
Event::listen('404', function()
{
return Response::error('404');
});
My styles aren't loaded. I assume this is because the laravel error controller isn't extending the base controller.
Any easy way around this without redeclaring my assets or something. I was assuming that declaring all my assets in the blade layout wasn't the best approach.
Ah ha!
I've just worked it out.
The answer is to use View Composers (one of the Laravel concepts I never properly investigated..)
So in my routes.php (temporary placement - I should probably place composers in their own auto-loaded file) I've just added the following.
View::composer(array('layout.master'), function($view)
{
Asset::container('header')->add('bootstrap-css', 'bundles/bootstrapper/css/bootstrap.min.css');
Asset::container('header')->add('main-css', 'css/style.css');
Asset::container('footer')->add('bootbox', 'js/vendor/bootbox.min.js');
});
Given that every frontend file I have is using /views/layout/master.blade.php then I know that I can apply assets to it by passing 'layout.master' to View::composer.
Et voila - now my 404 view is also loading the correct assets.
(I hope 'bootstrap' is the correct term...)
I have a Symfony 1.4 project in which I'm using a PHP script that mostly contains Javascript (I'm including this script with a simple <script src="/js/myStuff.js"></script> tag). I need to use some Symfony classes, helper methods, and variables from within the script (specifically the sfConfig class, url_for() helper method, and the $sf_request variable.) I'm at a loss as to how to achieve this. I tried copying the code from one of the front controllers into the script, but that ended up outputting the contents of my application's layout file.
Thanks in advance!
You can do what you want by using something like this to create a symfony context:
require_once($_SERVER['DOCUMENT_ROOT'].'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
$context = sfContext::createInstance($configuration);
To use url_for, you will also need to either load/include the Url helper, which can be done like:
sfContext::getInstance()->getConfiguration()->loadHelpers('Url');
I think there's a better approach though:
Serve this javascript file as symfony action if you need access to symfony - there's nothing that says you can only serve html through symfony.
Check out the block here entitled Javascript As An Action for an explanation ...
http://www.symfony-project.org/jobeet/1_2/Doctrine/en/18#chapter_18_user_feedback