Skip to content

Commit 2c2000a

Browse files
committed
first import of the "Official Best Practices" book
1 parent 69bfac1 commit 2c2000a

12 files changed

+2171
-0
lines changed

best_practices/business-logic.rst

+344
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 of 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)