Puzzlout / TrekToursFlyer

This is the Trek tours business flyer built with Symphony 3
MIT License
0 stars 0 forks source link

Project initialization #1

Closed WebDevJL closed 8 years ago

WebDevJL commented 8 years ago

@damirius,

When you are free and ready, please look at the following.

Since you are the most knowledgeable about Symphony, I need you to initialize the project with the most useful bundles and components of Symphony.

Also, please list how to:

More items may be added and please suggest anything that is important for a Symphony-based project.

Thanks.

WebDevJL commented 8 years ago

@damirius @gsouvik

I have added some info about loading and launching the solution for the first time in the repo's readme. Thanks.

damirius commented 8 years ago

@WebDevJL @gsouvik

Before we add anything else, I think we need to add some stuff to the .gitignore This is nice template https://github.com/github/gitignore/blob/master/Symfony.gitignore Since there are some bootstrap stuff and params which we should keep separate. Also since I'm using PHPStorm, it is creating /.idea directory which contains project info and configs for PHPStorm, I guess same goes for Eclipse and Netbeans, so it would be usefull if we add that stuff. Of course I believe we can ignore it locally if that's the problem.

damirius commented 8 years ago

@WebDevJL @gsouvik

Assets: OK, so everything should be stored in /web/ directory, since that's public available directory. Now if you will notice in that .gitignore template file /web/bundles/ and /web/uploads/ are ignored. That's because all stuff should be stored in the bundle directory and copied over before running. So we go in /src/ directory where our bundles reside. Let's say we want to use new bundle Puzzlout/FooBundle/. We go in /src/Puzzlout/FooBundle/ and create Resource directory if it isn't already there, inside we should create directory named public in lowercase, in that directory we should put all our static files. Before running app, we can install those assets in public web directory, by running "php bin/console assets:install --symlink" (note symlink param, it should work on OSX and Linux which will create symbolic link instead of hard copy). What this command will do is "copy" (or sym link) all stuff from public directory of our bundle into /web/bundles/puzzloutfoo/ directory, and we will be able to publicly access all those static files.

Bundles and Controllers generation: OK for Controllers is pretty straightforward, use this command php bin/console generate:controller It will guide you through interactive controller generation. You will first enter the name of the controller in this format BundleName:ControllerName, i.e. PuzzloutFooBundle:Product, or AppBundle:Product for default AppBundle. Other stuff it will ask you for is routing config format, we will go with annotations, template format I guess we will use twig and actions you want to add, you can skip actions since we will add them later on. That's it, new controller will be created in Controller directory with proper namespace and with Controller base class included and with Route annotation class if we chose annotations as our route config format. Now you can see how controller looks like in Controller directory in our Puzzlout/FooBundle/Controller directory. It extends base Controller class and it's named like NameController. Actions are named like NameAction. If you want to specify routes there like we should we use @Route("/path") annotation before each action, you can see one in DefaultController in our AppBundle.

For bundles command is php bin/console generate:bundle, as you noticed by now we will run bin/console commands often, they are really great. When running the command you will be prompted to state if bundle is shared or not, shared bundles are bundles which are to be used with multiple apps, like one in vendor directory and they should be prefixed with vendor name, I guess in our case Puzzlout/, name should be Puzzlout/NameBundle or just NameBundle if bundle isn't shared, i.e ForumBundle. After that we should just state that we want to create bundle in default /src directory and that we want to use annotations as config format. Skeleton will be created, for example /src/ForumBundle with ForumBundle.php inside or in shared bundle case /src/Puzzlout/ForumBundle with PuzzloutForumBundle.php. If you open it up you'll see namespace set relative to the src directory class name is in VendorNameBundle format and it extends base Bundle class. Now open up /app/config/routing.yml which is our main router config file. You'll see that we got new key under routes named vendor_name: (puzzlout_forum:, or forum:), below it should be resource which will contain actual routes, if we chose annotation it should be resource:"@ForumBundle/Controller", and after that prefix to the route we want, by default is '/'. That means that all routes stated in annotations in our controllers will be included by default. Last thing is our app/AppKernel.php file, it contains registerBundles() method. In $bundles array, there should be our newly created bundle. So this said, both controllers and bundles can be created manually. For controllers we only need to create controller file in Controller directory, setup namespace, include base classes we need and extend Controller. For bundles we must create directory skeleton and VendorNameBundle.php file which also contains proper namespace and extends Bundle class. After that register it in app/AppKernel.php and include routes in app/config/routing.yml

Doctrine: Doctrine comes with separate commands for generating entities, database and other stuff. Open up app/config/parameters.yml, we can setup params for db access here, I think it's all clear. Open up app/config/config.yml, find doctrine: key. You can change charset there if you wish and driver is set to pdo_mysql for mysql connection, other stuff is included through parameters.yml file (i.e. host: %database_host%). If you setup everything like you should you can run php bin/console doctrine:database:create it will create database you specified in parameters.yml with user you specified there in charset that you setup in config.yml. After that you are ready to generate entities for doctrine, run php bin/console doctrine:generate:entity, type entity name in format of NameBundle/EntityName (i.e. AppBundle:Product) choose annotations, you will then be prompted to enter name of the fields you want to add with their types and if they are unique or nullable. After you add all entities you want you can check them out in Entity directory of your Bundle. Everything should be generated together with getters and setters. To persist them in DB, run php bin/console doctrine:schema:update --force (force param will persist it in db, without it you'll get sql dump to double check before persisting if im not mistaken). You can also create them manually by creating Name.php file in Entity directory, take care of namespace and include Doctrine\ORM\Mapping as ORM; if you want to use annotations (check those automatically generated). For relations see http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html You can add manually new fields to existing entities, if you do you can always run php bin/console doctrine:generate:entities AppBundle/Product to update AppBundle/Product entity or php bin/console doctrine:generate entities AppBundle to update all entities inside AppBundle. When you update them don't worry if there are no changes on some of them they will stay the same, only new getters/setters will be created so no worries. Don't forget to run php bin/console doctrine:schema:update --force after each entity update so you persist those changes in files in the DB.

Deployment: You should point your virtualhost path to the web directory, remember that's publicly available directory. Example of my vhost setup for Apache: trek.txt If you rewrite it to app.php instead of app_dev.php you'll run it in production environment. Now it depends on your server configuration, if you are running php-fpm or maybe nginx or whatever, but they have really nice documentation with examples here http://symfony.com/doc/current/cookbook/configuration/web_server_configuration.html

I'll edit this post with more information

WebDevJL commented 8 years ago

@damirius

Go ahead with the .gitignore setup. You are free to push to the master branch for the moment.

I will read and comment the second post later.

Thanks for the details so far 👍

damirius commented 8 years ago

@WebDevJL

New .gitignore should be there, now the problem is, if I'm not mistaken, that we have to manually delete newly ignored files on the repository. If not they will still be tracked. I think we have to remove cached indexes, so something like "git rm -r --cached .", "git add ." and then just to commit it like that

damirius commented 8 years ago

Yes, that was the case, only I had to modify gitignore more since there were some issues with eclipse and symfony using same directory names for stuff. In any case it should be done, logs/cache/sessions should be removed from repository since we don't want them here for sure, along with few other files that are being created by composer.

WebDevJL commented 8 years ago

@damirius Thanks for the work.

Now, an additional question: can we manage partial views with Symphony?

Partial views could be:

Thanks for your insight.

damirius commented 8 years ago

@WebDevJL @gsouvik

Templating: Symfony uses Twig template engine. One of the most important Twig features is template inheritance, which let's us to extend base templates and decorate them, just like with OOP. Let's check default templates. Go in /app/Resource/views/default/base.html.twig, those views are default views for the whole App. When we create view for our, let's say, ProductController:IndexAction, we put it in /src/AppBundle/Resource/views/, best thing to do would be to store templates in separate subdirectories under views directory, so for example all stuff product related should be /src/AppBundle/Resource/views/product/index.html.twig. Let's take a quick look at the controller action, go in 'ProductController' or generate one if you wish and create 'IndexAction()' method, instead of returning text Response. We can return Response which contains rendered template. return $this->render("AppBundle:product:index.html.twig",array('var'=>$var)); So first argument is template path, second is array with key=>value pairs of stuff we want to forward down to template. In this example we can play with 'var' in our index.html.twig tempalte, which will contain the value of $var. We can pass different stuff, like objects, strings, integers whatever we want. That file should extend base file like this: {% extends '::base.html.twig' %}, note that twig file paths should be stated as Bundle:Directory:Filename, we can just put 'base.html.twig' instead of '::base.html.twig' since that's a default template, but we should use their naming conventions whenever we can. Also when we are extending templates, make sure that we put {% extends %} tag as first line in our twig file. When we extend the template, everything there should be shown on page when we render it. Now if you notice 'base.html.twig' contains some stuff called blocks, look at them as class methods that we could override. If we type in our 'product/index.html.twig' this line {% block title %} Products {% endblock %} we will override default block called title in our base class, so instead of having 'Welcome!' as our title, our page will have title 'Products'. Just like with OOP, sometimes when we override method from the class we are extending we still want to run the code from the super class. Let's take for example inclusion of js files. Base class has the block 'javascripts'. We can then include in our 'base.html.twig' some js files, like jquery or whatever we want, since we want it on every page. But we want to include more js files on specific page and we don't want to include it on all pages, for example gmaps.js. We just type {% block javascripts %} {{ parent() }} <script src='gmaps.js'></script> {% endblock %} *Note that we should use asset helper for linking assets, take a look on the end of the message to read about it Look at the {{ var }} as echo in php, and parent() will call parent block with the same name.

We can also include files which is something we need if we are talking partial views. If we want to include template file and pass some variables to it, we can use include tag. Open up 'product/index.html.twig'. In some block we can include any template we want. {% block body %} {{ include('AppBundle:includes:sidebar.html.twig',{'var':'passedvalue'}) }} Rest of the body {% endblock %} Now we create 'includes' directory (directory is optional as it's name) and inside we create sidebar.html.twig. In it just type something like This is included sidebar {{ var }}, echoing var should display 'passedvalue' string. If we change the include tag to {{ include('AppBundle:includes:sidebar.html.twig',{'var':var}) }}, we will pass var all the way down from controller to the included template. Now of course this is great for some static template inclusion or inclusion of files that depend on the variables that are passed through called template (in our case index.html.twig). For example instead of var we wanted to pass 'categories' and we don't want controller action which is rendering 'product/index.html.twig' to fetch all categories for us and pass it down. We want to decouple that stuff from our index action. Let's include in our ProductController one action that will fetch us some categories, let's call it "sidebarCategoriesAction" (remember that callable methods should end with "Action" keyword).

public function sidebarCategoriesAction(){
//fetch $categories logic
return $this->render("AppBundle:includes:sidebarcategories.html.twig",['categories'=>$categories]);
}

OK, so we need to create sidebarcategories.html.twig view in AppBundle/Resources/views/includes Open it up and type something like {% for category in categories %} Category: {{ category.name }}{% endfor %} (more about for tag http://twig.sensiolabs.org/doc/tags/for.html) Go in our 'product/index.html.twig', instead of {{ include(...) }} type this {{ render(controller('AppBundle:Product:sidebarCategories')) }} This will call that action, render the template sidebarcategories.html.twig and include it. Now we have totally decoupled, if you want to call it, partial module. If we want to pass down just some things that will modify the output of sidebar categories (let's say some filter), we can modify our sidebarCategoriesAction in our controller to have some params, let's say public function sidebarCategoriesAction($filter) and change our render call in product/index.html.twig to {{ render(controller('AppBundle:Product:sidebarCategories', {'filter':'somevalue'})) }}, that will call sidebarCategoriesAction('somevalue'). Few more things that are important, are pipeline filters for {{}} tag, for example we can quickly display passed down variable in lowercase by typing {{ varname | lower }} instead of doing that in controller since that's for display only. Twig autoescapes html stuff, so we don't need to escape script tags or something like that, but if we want to display it like that, we can use |raw filter. There are more useful filters, you can check them here http://twig.sensiolabs.org/doc/filters/index.html. For creating hyperlinks between pages, instead of using full paths, we can use path helper so we don't change it every time we decide to change the url of some controller. <a href="{{ path('routekey') }}>click here! </a>. routekey should be the name stated in @Route annotation for the controller action, @Route("/path/to/action",name="routekey"). Linking assets should be done like this <img src="{{ asset('imagesdir/pic.png') }}" />

Templates are compiled in classes and cached like that, so don't worry about it. In dev mode they are recompiled when they are changed but in production cache should be cleared so they can be recompiled and cached again. So everything will run smoothly. For more info on tags http://twig.sensiolabs.org/doc/tags/index.html

WebDevJL commented 8 years ago

@damirius Thanks for documenting the question in so many details. It seems that it does what we want. We will think about how to build our templates together.

Thanks.

WebDevJL commented 8 years ago

@damirius I'll close this. You can move to the wiki task. It will help to tidy up all the info in here.

I'm doing my best to assign you work but I have just 1h per day to do everything... Thanks for your patience.

WebDevJL commented 8 years ago

@damirius @gsouvik About answering some of the questions about coding submission, read this: https://github.com/Puzzlout/DevGuidelines/blob/master/IssueTrackerUsage.md