Skip to content

Commit 0c57939

Browse files
committed
feature #4327 First import of the "Official Best Practices" book (javiereguiluz)
This PR was merged into the 2.3 branch. Discussion ---------- First import of the "Official Best Practices" book As promised, this Pull Request adds the source files of the new "Official Best Practices" book recently published at symfony.com/best-practices. Similarly to any other Symfony documentation, the community is allowed, and even encouraged, to propose improvements and fixes to this document. However, given the special nature of this document, we'd like to clarify what proposals will be accepted and what proposals will not. **The following will be happily accepted:** * Typos and grammar fixes. * Objective errors (errors in code, technical arguments that are irrelevant or wrong, etc.) * Rewordings and simplifications of confusing explanations. * Proposals to add new best practices not covered so far. **The following will not make it:** * Complete rewriting of the existing best practices (for instance, a proposal to change *"use annotations ..."* to *"don't use annotations ..."* won't be accepted) Before some of you get mad at us, please let us explain the purpose of this book. The "Official Symfony Best Practices" explains the recommendations that the original creator of Symfony thinks you should follow to develop web applications. When we talk about opinions instead of facts, it's really hard to tell when someone is wrong. For instance, who of the following developers you think is wrong? * *Developer A*: you should not use annotations. They may be convenient for this and that, but they have these other problems. * *Developer B*: you should use annotations. They may have these problems, but it's worth it because of this and that convenience. For us, both opinions are valid, in the sense that they are not promoting universal truths. It's perfectly fine to think that annotations are good and/or bad. This is the reason why this book is not about the creator of Symfony thinking that he is smarter than all of us. And this is definitely not the list of perfect practices or mandatory practices. This is just an opinionated list of recommendations by the original creator of Symfony. If you happen to follow them (all or some) that's fine. If you don't want to follow them or even if you think that they are wrong, that's perfectly fine too. Let us make it clear again: this is not a list of universal truths or mandatory practices. This is just a list of opinions about how to do things. Follow them or not at your will. If the Symfony community wants to create their own set of "community best practices", that's great! That way, people will have more best practices to choose from and more information to make better decisions. We really hope things are more clear now. If not, please comment us your concerns and we'll better explain things for you. ------ Speaking from a strictly technical point of view, I'd like to warn documentation maintainers (@weaverryan, @wouterj and @xabbuh) that I have no idea about the process to follow when adding a new book to the Symfony documentation. The things that for sure are not going to work right now are the following: * On `symfony.com` we don't have the CSS style to display the best practices. We'll add it really soon to match the style of the existing PDF file. * On `symfony.com` the URL `http://symfony.com/doc/current/best_practices/index.html` now displays a regular web page. Once this book is added to the documentaton pipeline, we'll remove that page to show instead the index of this book. * The PDF generation for this book isn't prepared yet, so we'll have to wait a few days to set everything up. Commits ------- 7eb9367 Updated the sphinx submodule to ad support for best-practice directive d001da8 Fixed some typos reported by @henrikbjorn d12cfe5 Renamed "official best practices" to "best practices" af6b70b added the missing "best-practice" directive import in Sphinx configuration 30b9c45 updated fabpot/sphinx-php module reference f100fc0 Added an index directive to the first chapter d317925 Fixed some technical problems as suggested by Wouter 2c2000a first import of the "Official Best Practices" book
2 parents 6ceb8cb + 7eb9367 commit 0c57939

15 files changed

+2185
-2
lines changed

_exts

best_practices/business-logic.rst

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
Organizing Your Business Logic
2+
==============================
3+
4+
In computer software, **business logic** or domain logic is "the part of the
5+
program that encodes the real-world business rules that determine how data can
6+
be created, displayed, stored, and changed" (read `full definition`_).
7+
8+
In Symfony applications, business logic is all the custom code you write for
9+
your app that's not specific to the framework (e.g. routing and controllers).
10+
Domain classes, Doctrine entities and regular PHP classes that are used as
11+
services are good examples of business logic.
12+
13+
For most projects, you should store everything inside the ``AppBundle``.
14+
Inside here, you can create whatever directories you want to organize things:
15+
16+
.. code-block:: text
17+
18+
symfoy2-project/
19+
├─ app/
20+
├─ src/
21+
│ └─ AppBundle/
22+
│ └─ Utils/
23+
│ └─ MyClass.php
24+
├─ vendor/
25+
└─ web/
26+
27+
Storing Classes Outside of the Bundle?
28+
--------------------------------------
29+
30+
But there's no technical reason for putting business logic inside of a bundle.
31+
If you like, you can create your own namespace inside the ``src/`` directory
32+
and put things there:
33+
34+
.. code-block:: text
35+
36+
symfoy2-project/
37+
├─ app/
38+
├─ src/
39+
│ ├─ Acme/
40+
│ │ └─ Utils/
41+
│ │ └─ MyClass.php
42+
│ └─ AppBundle/
43+
├─ vendor/
44+
└─ web/
45+
46+
.. tip::
47+
48+
The recommended approach of using the ``AppBundle`` directory is for
49+
simplicity. If you're advanced enough to know what needs to live in
50+
a bundle and what can live outside of one, then feel free to do that.
51+
52+
Services: Naming and Format
53+
---------------------------
54+
55+
The blog application needs a utility that can transform a post title (e.g.
56+
"Hello World") into a slug (e.g. "hello-world"). The slug will be used as
57+
part of the post URL.
58+
59+
Let's, create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and
60+
add the following ``slugify()`` method:
61+
62+
.. code-block:: php
63+
64+
// src/AppBundle/Utils/Slugger.php
65+
namespace AppBundle\Utils;
66+
67+
class Slugger
68+
{
69+
public function slugify($string)
70+
{
71+
return preg_replace(
72+
'/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string)))
73+
);
74+
}
75+
}
76+
77+
Next, define a new service for that class.
78+
79+
.. code-block:: yaml
80+
81+
# app/config/services.yml
82+
services:
83+
# keep your service names short
84+
slugger:
85+
class: AppBundle\Utils\Slugger
86+
87+
Traditionally, the naming convention for a service involved following the
88+
class name and location to avoid name collisions. Thus, the service
89+
*would have been* called ``app.utils.slugger``. But by using short service names,
90+
your code will be easier to read and use.
91+
92+
.. best-practice::
93+
94+
The name of your application's services should be as short as possible,
95+
ideally just one simple word.
96+
97+
Now you can use the custom slugger in any controller class, such as the
98+
``AdminController``:
99+
100+
.. code-block:: php
101+
102+
public function createAction(Request $request)
103+
{
104+
// ...
105+
106+
if ($form->isSubmitted() && $form->isValid()) {
107+
$slug = $this->get('slugger')->slugify($post->getTitle()));
108+
$post->setSlug($slug);
109+
110+
// ...
111+
}
112+
}
113+
114+
Service Format: YAML
115+
--------------------
116+
117+
In the previous section, YAML was used to define the service.
118+
119+
.. best-practice::
120+
121+
Use the YAML format to define your own services.
122+
123+
This is controversial, and in our experience, YAML and XML usage is evenly
124+
distributed among developers, with a slight preference towards YAML.
125+
Both formats have the same performance, so this is ultimately a matter of
126+
personal taste.
127+
128+
We recommend YAML because it's friendly to newcomers and concise. You can
129+
of course use whatever format you like.
130+
131+
Service: No Class Parameter
132+
---------------------------
133+
134+
You may have noticed that the previous service definition doesn't configure
135+
the class namespace as a parameter:
136+
137+
.. code-block:: yaml
138+
139+
# app/config/services.yml
140+
141+
# service definition with class namespace as parameter
142+
parameters:
143+
slugger.class: AppBundle\Utils\Slugger
144+
145+
services:
146+
slugger:
147+
class: "%slugger.class%"
148+
149+
This practice is cumbersome and completely unnecessary for your own services:
150+
151+
.. best-practice::
152+
153+
Don't define parameters for the classes of your services.
154+
155+
This practice was wrongly adopted from third-party bundles. When Symfony
156+
introduced its service container, some developers used this technique to easily
157+
allow overriding services. However, overriding a service by just changing its
158+
class name is a very rare use case because, frequently, the new service has
159+
different constructor arguments.
160+
161+
Using a Persistence Layer
162+
-------------------------
163+
164+
Symfony is an HTTP framework that only cares about generating an HTTP response
165+
for each HTTP request. That's why Symfony doesn't provide a way to talk to
166+
a persistence layer (e.g. database, external API). You can choose whatever
167+
library or strategy you want for this.
168+
169+
In practice, many Symfony applications rely on the independent
170+
`Doctrine project`_ to define their model using entities and repositories.
171+
Just like with business logic, we recommend storing Doctrine entities in
172+
the ``AppBundle``
173+
174+
The three entities defined by our sample blog application are a good example:
175+
176+
.. code-block:: text
177+
178+
symfony2-project/
179+
├─ ...
180+
└─ src/
181+
└─ AppBundle/
182+
└─ Entity/
183+
├─ Comment.php
184+
├─ Post.php
185+
└─ User.php
186+
187+
.. tip::
188+
189+
If you're more advanced, you can of course store them under your own
190+
namespace in ``src/``.
191+
192+
Doctrine Mapping Information
193+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194+
195+
Doctrine Entities are plain PHP objects that you store in some "database".
196+
Doctrine only knows about your entities through the mapping metadata configured
197+
for your model classes. Doctrine supports four metadata formats: YAML, XML,
198+
PHP and annotations.
199+
200+
.. best-practice::
201+
202+
Use annotations to define the mapping information of the Doctrine entities.
203+
204+
Annotations are by far the most convenient and agile way of setting up and
205+
looking for mapping information:
206+
207+
.. code-block:: php
208+
209+
namespace AppBundle\Entity;
210+
211+
use Doctrine\ORM\Mapping as ORM;
212+
use Doctrine\Common\Collections\ArrayCollection;
213+
214+
/**
215+
* @ORM\Entity
216+
*/
217+
class Post
218+
{
219+
const NUM_ITEMS = 10;
220+
221+
/**
222+
* @ORM\Id
223+
* @ORM\GeneratedValue
224+
* @ORM\Column(type="integer")
225+
*/
226+
private $id;
227+
228+
/**
229+
* @ORM\Column(type="string")
230+
*/
231+
private $title;
232+
233+
/**
234+
* @ORM\Column(type="string")
235+
*/
236+
private $slug;
237+
238+
/**
239+
* @ORM\Column(type="text")
240+
*/
241+
private $content;
242+
243+
/**
244+
* @ORM\Column(type="string")
245+
*/
246+
private $authorEmail;
247+
248+
/**
249+
* @ORM\Column(type="datetime")
250+
*/
251+
private $publishedAt;
252+
253+
/**
254+
* @ORM\OneToMany(
255+
* targetEntity="Comment",
256+
* mappedBy="post",
257+
* orphanRemoval=true
258+
* )
259+
* @ORM\OrderBy({"publishedAt" = "ASC"})
260+
*/
261+
private $comments;
262+
263+
public function __construct()
264+
{
265+
$this->publishedAt = new \DateTime();
266+
$this->comments = new ArrayCollection();
267+
}
268+
269+
// getters and setters ...
270+
}
271+
272+
All formats have the same performance, so this is once again ultimately a
273+
matter of taste.
274+
275+
Data Fixtures
276+
~~~~~~~~~~~~~
277+
278+
As fixtures support is not enabled by default in Symfony, you should execute
279+
the following command to install the Doctrine fixtures bundle:
280+
281+
.. code-block:: bash
282+
283+
$ composer require "doctrine/doctrine-fixtures-bundle"
284+
285+
Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
286+
``test`` environments:
287+
288+
.. code-block:: php
289+
290+
use Symfony\Component\HttpKernel\Kernel;
291+
292+
class AppKernel extends Kernel
293+
{
294+
public function registerBundles()
295+
{
296+
$bundles = array(
297+
// ...
298+
);
299+
300+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
301+
// ...
302+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
303+
}
304+
305+
return $bundles;
306+
}
307+
308+
// ...
309+
}
310+
311+
We recommend creating just *one* `fixture class`_ for simplicity, though
312+
you're welcome to have more if that class gets quite large.
313+
314+
Assuming you have at least one fixtures class and that the database access
315+
is configured properly, you can load your fixtures by executing the following
316+
command:
317+
318+
.. code-block:: bash
319+
320+
$ php app/console doctrine:fixtures:load
321+
322+
Careful, database will be purged. Do you want to continue Y/N ? Y
323+
> purging database
324+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
325+
326+
Coding Standards
327+
----------------
328+
329+
The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
330+
were defined by the PHP community. You can learn more about
331+
`the Symfony Code Standards`_ and even use the `PHP-CS-Fixer`_, which is
332+
a command-line utility that can fix the coding standards of an entire codebase
333+
in a matter of seconds.
334+
335+
.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
336+
.. _`Toran Proxy`: https://toranproxy.com/
337+
.. _`Composer`: https://getcomposer.org/
338+
.. _`MVC architecture`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
339+
.. _`Doctrine project`: http://www.doctrine-project.org/
340+
.. _`fixture class`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
341+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
342+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
343+
.. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html
344+
.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer

0 commit comments

Comments
 (0)