Skip to content

Commit b1cd35b

Browse files
committed
Merge branch 'master' into abstract-voter
Conflicts: cookbook/security/voters_data_permission.rst
2 parents da32a5e + 87eb36c commit b1cd35b

File tree

348 files changed

+13998
-7541
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

348 files changed

+13998
-7541
lines changed

.gitignore

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
11
/_build
2-
/bundles/DoctrineFixturesBundle
3-
/bundles/DoctrineMigrationsBundle
4-
/bundles/DoctrineMongoDBBundle
5-
/bundles/SensioFrameworkExtraBundle
6-
/bundles/SensioGeneratorBundle
7-
/cmf
82
/_exts

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ language: python
33
python:
44
- "2.7"
55

6+
sudo: false
7+
68
install:
7-
- "bash install.sh"
89
- "pip install -q -r requirements.txt --use-mirrors"
910

1011
script: sphinx-build -nW -b html -d _build/doctrees . _build/html

best_practices/business-logic.rst

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
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+
symfony2-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+
symfony2-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+
app.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+
but unique enough that you can search your project for the service if
96+
you ever need to.
97+
98+
Now you can use the custom slugger in any controller class, such as the
99+
``AdminController``:
100+
101+
.. code-block:: php
102+
103+
public function createAction(Request $request)
104+
{
105+
// ...
106+
107+
if ($form->isSubmitted() && $form->isValid()) {
108+
$slug = $this->get('app.slugger')->slugify($post->getTitle());
109+
$post->setSlug($slug);
110+
111+
// ...
112+
}
113+
}
114+
115+
Service Format: YAML
116+
--------------------
117+
118+
In the previous section, YAML was used to define the service.
119+
120+
.. best-practice::
121+
122+
Use the YAML format to define your own services.
123+
124+
This is controversial, and in our experience, YAML and XML usage is evenly
125+
distributed among developers, with a slight preference towards YAML.
126+
Both formats have the same performance, so this is ultimately a matter of
127+
personal taste.
128+
129+
We recommend YAML because it's friendly to newcomers and concise. You can
130+
of course use whatever format you like.
131+
132+
Service: No Class Parameter
133+
---------------------------
134+
135+
You may have noticed that the previous service definition doesn't configure
136+
the class namespace as a parameter:
137+
138+
.. code-block:: yaml
139+
140+
# app/config/services.yml
141+
142+
# service definition with class namespace as parameter
143+
parameters:
144+
slugger.class: AppBundle\Utils\Slugger
145+
146+
services:
147+
app.slugger:
148+
class: "%slugger.class%"
149+
150+
This practice is cumbersome and completely unnecessary for your own services:
151+
152+
.. best-practice::
153+
154+
Don't define parameters for the classes of your services.
155+
156+
This practice was wrongly adopted from third-party bundles. When Symfony
157+
introduced its service container, some developers used this technique to easily
158+
allow overriding services. However, overriding a service by just changing its
159+
class name is a very rare use case because, frequently, the new service has
160+
different constructor arguments.
161+
162+
Using a Persistence Layer
163+
-------------------------
164+
165+
Symfony is an HTTP framework that only cares about generating an HTTP response
166+
for each HTTP request. That's why Symfony doesn't provide a way to talk to
167+
a persistence layer (e.g. database, external API). You can choose whatever
168+
library or strategy you want for this.
169+
170+
In practice, many Symfony applications rely on the independent
171+
`Doctrine project`_ to define their model using entities and repositories.
172+
Just like with business logic, we recommend storing Doctrine entities in the
173+
AppBundle.
174+
175+
The three entities defined by our sample blog application are a good example:
176+
177+
.. code-block:: text
178+
179+
symfony2-project/
180+
├─ ...
181+
└─ src/
182+
└─ AppBundle/
183+
└─ Entity/
184+
├─ Comment.php
185+
├─ Post.php
186+
└─ User.php
187+
188+
.. tip::
189+
190+
If you're more advanced, you can of course store them under your own
191+
namespace in ``src/``.
192+
193+
Doctrine Mapping Information
194+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195+
196+
Doctrine Entities are plain PHP objects that you store in some "database".
197+
Doctrine only knows about your entities through the mapping metadata configured
198+
for your model classes. Doctrine supports four metadata formats: YAML, XML,
199+
PHP and annotations.
200+
201+
.. best-practice::
202+
203+
Use annotations to define the mapping information of the Doctrine entities.
204+
205+
Annotations are by far the most convenient and agile way of setting up and
206+
looking for mapping information:
207+
208+
.. code-block:: php
209+
210+
namespace AppBundle\Entity;
211+
212+
use Doctrine\ORM\Mapping as ORM;
213+
use Doctrine\Common\Collections\ArrayCollection;
214+
215+
/**
216+
* @ORM\Entity
217+
*/
218+
class Post
219+
{
220+
const NUM_ITEMS = 10;
221+
222+
/**
223+
* @ORM\Id
224+
* @ORM\GeneratedValue
225+
* @ORM\Column(type="integer")
226+
*/
227+
private $id;
228+
229+
/**
230+
* @ORM\Column(type="string")
231+
*/
232+
private $title;
233+
234+
/**
235+
* @ORM\Column(type="string")
236+
*/
237+
private $slug;
238+
239+
/**
240+
* @ORM\Column(type="text")
241+
*/
242+
private $content;
243+
244+
/**
245+
* @ORM\Column(type="string")
246+
*/
247+
private $authorEmail;
248+
249+
/**
250+
* @ORM\Column(type="datetime")
251+
*/
252+
private $publishedAt;
253+
254+
/**
255+
* @ORM\OneToMany(
256+
* targetEntity="Comment",
257+
* mappedBy="post",
258+
* orphanRemoval=true
259+
* )
260+
* @ORM\OrderBy({"publishedAt" = "ASC"})
261+
*/
262+
private $comments;
263+
264+
public function __construct()
265+
{
266+
$this->publishedAt = new \DateTime();
267+
$this->comments = new ArrayCollection();
268+
}
269+
270+
// getters and setters ...
271+
}
272+
273+
All formats have the same performance, so this is once again ultimately a
274+
matter of taste.
275+
276+
Data Fixtures
277+
~~~~~~~~~~~~~
278+
279+
As fixtures support is not enabled by default in Symfony, you should execute
280+
the following command to install the Doctrine fixtures bundle:
281+
282+
.. code-block:: bash
283+
284+
$ composer require "doctrine/doctrine-fixtures-bundle"
285+
286+
Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
287+
``test`` environments:
288+
289+
.. code-block:: php
290+
291+
use Symfony\Component\HttpKernel\Kernel;
292+
293+
class AppKernel extends Kernel
294+
{
295+
public function registerBundles()
296+
{
297+
$bundles = array(
298+
// ...
299+
);
300+
301+
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
302+
// ...
303+
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
304+
}
305+
306+
return $bundles;
307+
}
308+
309+
// ...
310+
}
311+
312+
We recommend creating just *one* `fixture class`_ for simplicity, though
313+
you're welcome to have more if that class gets quite large.
314+
315+
Assuming you have at least one fixtures class and that the database access
316+
is configured properly, you can load your fixtures by executing the following
317+
command:
318+
319+
.. code-block:: bash
320+
321+
$ php app/console doctrine:fixtures:load
322+
323+
Careful, database will be purged. Do you want to continue Y/N ? Y
324+
> purging database
325+
> loading AppBundle\DataFixtures\ORM\LoadFixtures
326+
327+
Coding Standards
328+
----------------
329+
330+
The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
331+
were defined by the PHP community. You can learn more about
332+
:doc:`the Symfony Coding standards </contributing/code/standards>` and even
333+
use the `PHP-CS-Fixer`_, which is a command-line utility that can fix the
334+
coding standards of an entire codebase in a matter of seconds.
335+
336+
.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic
337+
.. _`Doctrine project`: http://www.doctrine-project.org/
338+
.. _`fixture class`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
339+
.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
340+
.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
341+
.. _`PHP-CS-Fixer`: https://github.com/FriendsOfPHP/PHP-CS-Fixer

0 commit comments

Comments
 (0)