diff --git a/guides/doctrine/configuration.rst b/guides/doctrine/configuration.rst index 6c0927b7bcf..c4e47851622 100644 --- a/guides/doctrine/configuration.rst +++ b/guides/doctrine/configuration.rst @@ -4,8 +4,12 @@ Doctrine Configuration DBAL Configuration ------------------ +<<<<<<< HEAD:guides/doctrine/configuration.rst .. code-block:: yaml +======= + [yml] +>>>>>>> origin/PR3:guides/en/Doctrine/Configuration.markdown # config/config.yml doctrine.dbal: driver: PDOMySql @@ -64,7 +68,18 @@ but you must pass it an argument with the name of the connection you want to get ORM Configuration ----------------- +<<<<<<< HEAD:guides/doctrine/configuration.rst .. code-block:: yaml +======= + doctrine.orm: + default_entity_manager: default + cache_driver: apc # array, apc, memcache, xcache + entity_managers: + default: + connection: default + customer: + connection: customer +>>>>>>> origin/PR3:guides/en/Doctrine/Configuration.markdown doctrine.orm: default_entity_manager: default @@ -150,4 +165,4 @@ or options: :drop Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output. :update Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output. - ... + ... \ No newline at end of file diff --git a/guides/pt_BR/Twig.rst b/guides/pt_BR/Twig.rst new file mode 100644 index 00000000000..dfb1d39f3ef --- /dev/null +++ b/guides/pt_BR/Twig.rst @@ -0,0 +1,140 @@ +.. index:: + single: Twig + single: View; Twig + +Twig & Symfony2 +=============== + +`Twig`_ is a flexible, fast, and secure template language for PHP. Symfony2 +has native support for Twig through ``TwigBundle``. + +.. index:: + single: Twig; Installation + single: Twig; Configuration + +Installation & Configuration +---------------------------- + +Enable the ``TwigBundle`` in your kernel:: + + public function registerBundles() + { + $bundles = array( + // ... + new Symfony\Framework\TwigBundle\Bundle(), + ); + + // ... + } + +Then, configure it: + +.. code-block:: yaml + + # config/config.yml + twig.config: ~ + + # config/config_dev.yml + twig.config: + auto_reload: true + +.. tip:: + The configuration options are the same as the ones you pass to the + ``Twig_Environment`` `constructor`_. + +Usage +----- + +To render a Twig template instead of a PHP one, add the ``:twig`` suffix at the +end of the template name. The controller below renders the ``index.twig`` +template:: + + public function indexAction($name) + { + return $this->render('HelloBundle:Hello:index:twig', array('name' => $name)); + } + +The ``:twig`` suffix is only needed when there is no context, like in a +controller. But when you extend or include a template from a Twig template, +Symfony2 automatically switches the default engine to Twig: + +.. code-block:: jinja + + {# index.twig #} + + {# no need to add :twig as this is the default #} + {% extends 'HelloBundle::layout' %} + + {% block content %} + Hello {{ name }} + + {# use the special render tag to render a template #} + {% render 'HelloBundle:Hello:sidebar' %} + {% endblock %} + +To embed a PHP template in a Twig one, add the ``:php`` suffix to the template +name: + +.. code-block:: jinja + + {# index.twig #} + + {% render 'HelloBundle:Hello:sidebar:php' %} + +And the opposite is also true:: + + {# index.php #} + + render('HelloBundle:Hello:sidebar:twig') ?> + +.. index:: + single: Twig; Helpers + +Helpers +------- + +The default Symfony2 helpers are available within a Twig template via +specialized tags: + +.. code-block:: jinja + + {# add a javascript #} + {% javascript 'bundles/blog/js/blog.js' %} + + {# add a stylesheet #} + {% stylesheet 'bundles/blog/css/blog.css' with ['media': 'screen'] %} + + {# output the javascripts and stylesheets in the layout #} + {% javascripts %} + {% stylesheets %} + + {# generate a URL for an asset #} + {% asset 'css/blog.css' %} + {% asset 'images/logo.png' %} + + {# generate a route #} + {% route 'blog_post' with ['id': post.id] %} + + {# render a template #} + {% include 'BlogBundle:Post:list' %} + + {# embed another controller response #} + {% render 'BlogBundle:Post:list' with ['path': ['limit': 2], 'alt': 'BlogBundle:Post:error'] %} + +.. _twig_extensions:: + +Enabling Custom Extensions +-------------------------- + +To enable a Twig extension, add it as a regular service in one of your +configuration, and add a ``twig.extension`` annotation: + +.. code-block:: yaml + + services: + twig.extension.your_extension_name: + class: Fully\Qualified\Extension\Class\Name + annotation: { name: twig.extension } + +.. _Twig: http://www.twig-project.org/ +.. _constructor: http://www.twig-project.org/book/03-Twig-for-Developers diff --git a/guides/pt_BR/bundles/best_practices.rst b/guides/pt_BR/bundles/best_practices.rst new file mode 100644 index 00000000000..0898389b8d6 --- /dev/null +++ b/guides/pt_BR/bundles/best_practices.rst @@ -0,0 +1,175 @@ +.. index:: + single: Bundles; Best Practices + +Bundle Best Practices +===================== + +A bundle is a directory that has a well-defined structure and can host +anything from classes to controllers and web resources. Even if bundles are +very flexible, you should follow some best practices if you want to distribute +them. + +.. index:: + pair: Bundles; Naming Conventions + +Bundle Name +----------- + +A bundle is also a PHP namespace, composed of several segments: + +* The **main namespace**: either ``Bundle``, for reusable bundles, or + ``Application`` for application specific bundles; +* The **vendor namespace** (optional for ``Application`` bundles): something + unique to you or your company (like ``Sensio``); +* *(optional)* The **category namespace(s)** to better organize a large set of + bundles; +* The **bundle name**. + +.. caution:: + The vendor namespace and the category namespaces are only possible as of + Symfony2 PR3. + +The bundle name must follow the following rules: + +* Use only alphanumeric characters and underscores; +* Use a CamelCased name; +* Use a descriptive and short name (no more than 2 words); +* Prefix the name with the concatenation of the vendor and category + namespaces; +* Suffix the name with ``Bundle``. + +Some good bundle names: + +=================================== ========================== +Namespace Bundle Name +=================================== ========================== +``Bundle\Sensio\BlogBundle`` ``SensioBlogBundle`` +``Bundle\Sensio\Social\BlogBundle`` ``SensioSocialBlogBundle`` +``Application\BlogBundle`` ``BlogBundle`` +=================================== ========================== + +Directory Structure +------------------- + +The basic directory structure of a ``HelloBundle`` bundle must read as +follows:: + + XXX/... + HelloBundle/ + HelloBundle.php + Controller/ + Resources/ + meta/ + LICENSE + config/ + doc/ + index.rst + views/ + web/ + Tests/ + +The ``XXX`` directory(ies) reflects the namespace structure of the bundle. + +The following files are mandatory: + +* ``HelloBundle.php``; +* ``Resources/meta/LICENSE``: The full license for the code; +* ``Resources/doc/index.rst``: The root file for the Bundle documentation. + +.. note:: + These conventions ensure that automated tools can rely on this default + structure to work. + +The depth of sub-directories should be kept to the minimal for most used +classes and files (2 levels at a maximum). More levels can be defined for +non-strategic, less-used files. + +The bundle directory is read-only. If you need to write temporary files, store +them under the ``cache/`` or ``log/`` directory of the host application. Tools can +generate files in the bundle directory structure, but only if the generated +files are going to be part of the repository. + +The following classes and files have specific emplacements: + +========================= ===================== +Type Directory +========================= ===================== +Controllers ``Controller/`` +Templates ``Resources/views/`` +Unit and Functional Tests ``Tests/`` +Web Resources ``Resources/web/`` +Configuration ``Resources/config/`` +Commands ``Command/`` +========================= ===================== + +Classes +------- + +The bundle directory structure is used as the namespace hierarchy. For +instance, a ``HelloController`` controller is stored in +``Bundle/HelloBundle/Controller/HelloController.php`` and the fully qualified +class name is ``Bundle\HelloBundle\Controller\HelloController``. + +All classes and files must follow the Symfony2 coding `standards`_. + +Some classes should be seen as facades and should be as short as possible, +like Commands, Helpers, Listeners, and Controllers. + +Classes that connects to the Event Dispatcher should have a name that ends +with ``Listener``. + +Exceptions classes should be stored in an ``Exception`` sub-namespace. + +Vendors +------- + +A bundle must not embed third-party PHP libraries. It should rely on the +standard Symfony2 autoloading instead. + +A bundle should not embed third-party libraries written in JavaScript, CSS, or +any other language. + +Tests +----- + +A bundle should come with a test suite written with PHPUnit and stored under +the ``Tests/`` directory. Tests should follow the following principles: + +* The test suite must be executable with a simple ``phpunit`` command run from + a sample application; +* The functional tests should only be used to test the response output and + some profiling information if you have some; +* The code coverage should at least covers 95% of the code base. + +.. note:: + A test suite must not contain ``AllTests.php`` scripts, but must rely on the + existence of a ``phpunit.xml.dist`` file. + +Documentation +------------- + +All classes and functions must come with full PHPDoc. + +Extensive documentation should also be provided in the :doc:`reStructuredText +` format, under the ``Resources/doc/`` +directory; the ``Resources/doc/index.rst`` file is the only mandatory file. + +Templates +--------- + +If a bundle provides templates, they should be defined in plain PHP. A bundle +must not provide a main layout, but extends a default ``base`` template (which +must provide two slots: ``content`` and ``head``). + +.. note:: + The only other template engine supported is Twig, but only for specific + cases. + +Configuration +------------- + +Configuration must be done via the Symfony2 built-in `mechanism`_. A bundle +should provide all its default configurations in XML. + +.. _standards: http://www.symfony-reloaded.org/contributing/Code/Standards +.. _mechanism: http://www.symfony-reloaded.org/guides/Bundles/Configuration diff --git a/guides/pt_BR/bundles/configuration.rst b/guides/pt_BR/bundles/configuration.rst new file mode 100644 index 00000000000..0b3ba90d734 --- /dev/null +++ b/guides/pt_BR/bundles/configuration.rst @@ -0,0 +1,114 @@ +.. index:: + single: Bundles; Configuration + +Bundle Configuration +==================== + +To provide more flexibility, a bundle can provide configurable settings by +using the Symfony2 built-in mechanisms. + +Simple Configuration +-------------------- + +For simple configuration settings, rely on the default ``parameters`` entry of +the Symfony2 configuration. Symfony2 parameters are simple key/value pairs; a +value being any valid PHP value. Each parameter name must start with a +lower-cased version of the bundle name (``hello`` for ``HelloBundle``, or +``sensio.social.blog`` for ``Sensio\Social\BlogBundle`` for instance). + +The end user can provide values in any XML/YAML/INI configuration file: + +.. code-block:: xml + + + + fabien@example.com + + +.. code-block:: yaml + + parameters: + hello.email.from: fabien@example.com + +.. code-block:: ini + + [parameters] + hello.email.from=fabien@example.com + +Retrieve the configuration parameters in your code from the container:: + + $container->getParameter('hello.email.from'); + +Even if this mechanism is simple enough, you are highly encouraged to use the +semantic configuration described below. + +.. index:: + single: Configuration; Semantic + single: Bundle; Extension Configuration + +Semantic Configuration +---------------------- + +Semantic configuration provides an even more flexible way to provide +configuration for a bundle with the following advantages over simple +parameters: + +* Possibility to define more than just configuration (services for + instance); +* Better hierarchy in the configuration (you can define nested + configuration); +* Smart merging when several configuration files override an existing + configuration; +* Configuration validation (if you define an XSD file and use XML); +* Completion when you use XSD and XML. + +To define a semantic configuration, create a Dependency Injection extension:: + + // HelloBundle/DependencyInjection/HelloExtension.php + class DocExtension extends LoaderExtension + { + public function configLoad($config) + { + // ... + } + + public function getXsdValidationBasePath() + { + return __DIR__.'/../Resources/config/'; + } + + public function getNamespace() + { + return 'http://www.example.com/symfony/schema/'; + } + + public function getAlias() + { + return 'hello'; + } + } + +Follow these rules: + +* The extension must be stored in the ``DependencyInjection`` sub-namespace; +* The extension must be named after the bundle name and suffixed with + ``Extension`` (``HelloExtension`` for ``HelloBundle``); +* The alias must be unique and named after the bundle name (``hello`` for + ``HelloBundle`` or ``sensio.social.blog`` for ``Sensio\Social\BlogBundle``); +* The extension should provide an XSD schema. + +Eventually, register the extension:: + + class HelloBundle extends BaseBundle + { + public function buildContainer(ContainerInterface $container) + { + Loader::registerExtension(new HelloExtension()); + } + } + +Naming Conventions +------------------ + +All parameter and service names starting with a ``_`` are reserved for the +framework, and new ones must not be defined by bundles. diff --git a/guides/pt_BR/bundles/index.rst b/guides/pt_BR/bundles/index.rst new file mode 100644 index 00000000000..6617561e724 --- /dev/null +++ b/guides/pt_BR/bundles/index.rst @@ -0,0 +1,8 @@ +Bundles +======= + +.. toctree:: + :maxdepth: 2 + + best_practices + configuration diff --git a/guides/pt_BR/doctrine/MongoDB.rst b/guides/pt_BR/doctrine/MongoDB.rst new file mode 100644 index 00000000000..6e0cb986533 --- /dev/null +++ b/guides/pt_BR/doctrine/MongoDB.rst @@ -0,0 +1,253 @@ +.. index:: + single: Doctrine; MongoDB + single: MongoDB + +MongoDB +======= + +The `MongoDB`_ Object Document Mapper is much like the Doctrine2 ORM in the +way it works and architecture. You only deal with plain PHP objects and they are persisted +transparently without imposing on your domain model. + +.. tip:: + You can read more about the Doctrine MongoDB Object Document Mapper on the + projects `documentation`_. + +Configuration +------------- + +To get started working with Doctrine and the MongoDB Object Document Mapper you just need +to enable it: + +.. code-block:: yaml + + # config/config.yml + doctrine_odm.mongodb: ~ + +The above YAML is the most simple example and uses all of the default values provided, if +you need to customize more you can specify the complete configuration: + +.. code-block:: yaml + + # config/config.yml + doctrine_odm.mongodb: + server: mongodb://localhost:27017 + options: + connect: true + metadata_cache_driver: array # array, apc, xcache, memcache + +If you wish to use memcache to cache your metadata and you need to configure the ``Memcache`` instance you can do the following: + +.. code-block:: yaml + + # config/config.yml + doctrine_odm.mongodb: + server: mongodb://localhost:27017 + options: + connect: true + metadata_cache_driver: + type: memcache + class: Doctrine\Common\Cache\MemcacheCache + host: localhost + port: 11211 + instance_class: Memcache + +Multiple Connections +~~~~~~~~~~~~~~~~~~~~ + +If you need multiple connections and document managers you can use the following syntax: + +.. code-block:: yaml + + doctrine_odm.mongodb: + default_connection: conn2 + default_document_manager: dm2 + metadata_cache_driver: apc + connections: + conn1: + server: mongodb://localhost:27017 + options: + connect: true + conn2: + server: mongodb://localhost:27017 + options: + connect: true + document_managers: + dm1: + connection: conn1 + metadata_cache_driver: xcache + dm2: + connection: conn2 + +Now you can retrieve the configured services connection services:: + + $conn1 = $container->getService('doctrine.odm.mongodb.conn1_connection'); + $conn2 = $container->getService('doctrine.odm.mongodb.conn2_connection'); + +And you can also retrieve the configured document manager services which utilize the above +connection services:: + + $dm1 = $container->getService('doctrine.odm.mongodb.dm1_connection'); + $dm2 = $container->getService('doctrine.odm.mongodb.dm1_connection'); + +XML +~~~ + +You can specify the same configuration via XML if you prefer that. Here are the same +examples from above in XML. + +Simple Single Connection: + +.. code-block:: xml + + + + + + + + Doctrine\Common\Cache\MemcacheCache + localhost + 11211 + Memcache + + + true + + + + +Multiple Connections: + +.. code-block:: xml + + + + + + + + + + true + + + + + true + + + + + + + + + + +Writing Document Classes +------------------------ + +You can start writing document classes just how you normally would write some PHP classes. +The only difference is that you must map the classes to the MongoDB ODM. You can provide +the mapping information via xml, yaml or annotations. In this example, for simplicity and +ease of reading we will use annotations. + +First, lets write a simple User class:: + + // src/Application/HelloBundle/Document/User.php + + namespace Application\HelloBundle\Document; + + class User + { + protected $id; + protected $name; + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + } + +This class can be used independent from any persistence layer as it is a regular PHP +class and does not have any dependencies. Now we need to annotate the class so Doctrine +can read the annotated mapping information from the doc blocks:: + + // ... + + /** @Document(collection="users") */ + class User + { + /** @Id */ + protected $id; + + /** @String */ + protected $name; + + // ... + } + +Using Documents +--------------- + +Now that you have a PHP class that has been mapped properly you can begin working with +instances of that document persisting to and retrieving from MongoDB. + +From your controllers you can access the ``DocumentManager`` instances from +the container:: + + class UserController extends Controller + { + public function createAction() + { + $user = new User(); + $user->setName('Jonathan H. Wage'); + + $dm = $this->container->getService('doctrine.odm.mongodb.document_manager'); + $dm->persist($user); + $dm->flush(); + + // ... + } + } + +Later you can retrieve the persisted document by its id:: + + class UserController extends Controller + { + public function editAction($id) + { + $dm = $this->container->getService('doctrine.odm.mongodb.document_manager'); + $user = $dm->find('HelloBundle:User', $id); + + // ... + } + } + +.. _MongoDB: http://www.mongodb.org/ +.. _documentation: http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/en diff --git a/guides/pt_BR/doctrine/configuration.rst b/guides/pt_BR/doctrine/configuration.rst new file mode 100644 index 00000000000..c4e47851622 --- /dev/null +++ b/guides/pt_BR/doctrine/configuration.rst @@ -0,0 +1,168 @@ +Doctrine Configuration +====================== + +DBAL Configuration +------------------ + +<<<<<<< HEAD:guides/doctrine/configuration.rst +.. code-block:: yaml + +======= + [yml] +>>>>>>> origin/PR3:guides/en/Doctrine/Configuration.markdown + # config/config.yml + doctrine.dbal: + driver: PDOMySql + dbname: Symfony2 + user: root + password: null + +You can also specify some additional configurations on a connection but they +are not required: + +.. code-block:: yaml + + # ... + + doctrine.dbal: + # ... + + host: localhost + port: ~ + path: %kernel.data_dir%/symfony.sqlite + event_manager_class: Doctrine\Common\EventManager + configuration_class: Doctrine\DBAL\Configuration + wrapper_class: ~ + options: [] + +If you want to configure multiple connections you can do so by simply listing them under +the key named ``connections``: + +.. code-block:: yaml + + doctrine.dbal: + default_connection: default + connections: + default: + dbname: Symfony2 + user: root + password: null + host: localhost + customer: + dbname: customer + user: root + password: null + host: localhost + +If you have defined multiple connections you can use the ``getDatabaseConnection()`` as well +but you must pass it an argument with the name of the connection you want to get:: + + class UserController extends Controller + { + public function indexAction() + { + $conn = $this->container->getDatabaseConnection('customer'); + } + } + +ORM Configuration +----------------- + +<<<<<<< HEAD:guides/doctrine/configuration.rst +.. code-block:: yaml +======= + doctrine.orm: + default_entity_manager: default + cache_driver: apc # array, apc, memcache, xcache + entity_managers: + default: + connection: default + customer: + connection: customer +>>>>>>> origin/PR3:guides/en/Doctrine/Configuration.markdown + + doctrine.orm: + default_entity_manager: default + cache_driver: apc # array, apc, memcache, xcache + entity_managers: + default: + connection: default + customer: + connection: customer + +Just like the DBAL, if you have configured multiple ``EntityManager`` instances and want to +get a specific one you can use the ``getEntityManager()`` method by just passing it an argument +that is the name of the ``EntityManager`` you want:: + + class UserController extends Controller + { + public function indexAction() + { + $em = $this->container->getService('doctrine.orm.customer_entity_manager'); + } + } + +Now the scenario arrises where you want to change your mapping information and +update your development database schema without blowing away everything and +losing your existing data. So first lets just add a new property to our ``User`` +entity:: + + namespace Application\HelloBundle\Entities; + + /** @Entity */ + class User + { + /** @Column(type="string") */ + protected $new; + + // ... + } + +Once you've done that, to get your database schema updated with the new column +you just need to run the following command: + + $ php hello/console doctrine:schema:update + +Now your database will be updated and the new column added to the database +table. + +Console Commands +---------------- + +The Doctrine2 ORM integration offers several console commands under the ``doctrine`` +namespace. To view a list of the commands you can run the console without any arguments +or options: + + $ php hello/console + ... + + doctrine + :ensure-production-settings Verify that Doctrine is properly configured for a production environment. + :schema-tool Processes the schema and either apply it directly on EntityManager or generate the SQL output. + doctrine:cache + :clear-metadata Clear all metadata cache for a entity manager. + :clear-query Clear all query cache for a entity manager. + :clear-result Clear result cache for a entity manager. + doctrine:data + :load Load data fixtures to your database. + doctrine:database + :create Create the configured databases. + :drop Drop the configured databases. + doctrine:generate + :entities Generate entity classes and method stubs from your mapping information. + :entity Generate a new Doctrine entity inside a bundle. + :proxies Generates proxy classes for entity classes. + :repositories Generate repository classes from your mapping information. + doctrine:mapping + :convert Convert mapping information between supported formats. + :convert-d1-schema Convert a Doctrine1 schema to Doctrine2 mapping files. + :import Import mapping information from an existing database. + doctrine:query + :dql Executes arbitrary DQL directly from the command line. + :sql Executes arbitrary SQL directly from the command line. + doctrine:schema + :create Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output. + :drop Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output. + :update Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output. + + ... \ No newline at end of file diff --git a/guides/pt_BR/doctrine/index.rst b/guides/pt_BR/doctrine/index.rst new file mode 100644 index 00000000000..bfcd552931a --- /dev/null +++ b/guides/pt_BR/doctrine/index.rst @@ -0,0 +1,9 @@ +Doctrine +======== + +.. toctree:: + :maxdepth: 2 + + Overview + migrations + MongoDB diff --git a/guides/pt_BR/doctrine/migrations.rst b/guides/pt_BR/doctrine/migrations.rst new file mode 100644 index 00000000000..fb43fd7f02e --- /dev/null +++ b/guides/pt_BR/doctrine/migrations.rst @@ -0,0 +1,107 @@ +.. index:: + single: Doctrine; Migrations + +Doctrine Migrations +=================== + +The database migrations feature is an extension of the database abstraction +layer and offers you the ability to programmatically deploy new versions of +your database schema in a safe and standardized way. + +.. tip:: + You can read more about the Doctrine Database Migrations on the projects + `documentation`_. + +All of the migrations functionality is contained in a few console commands: + +.. code-block:: bash + + doctrine:migrations + :diff Generate a migration by comparing your current database to your mapping information. + :execute Execute a single migration version up or down manually. + :generate Generate a blank migration class. + :migrate Execute a migration to a specified version or the latest available version. + :status View the status of a set of migrations. + :version Manually add and delete migration versions from the version table. + +Every bundle manages its own migrations so when working with the above +commands you must specify the bundle you want to work with. For example to see +the status of a bundles migrations you can run the ``status`` command: + +.. code-block:: bash + + $ php console doctrine:migrations:status --bundle="Application\HelloBundle" + + == Configuration + + >> Name: HelloBundle Migrations + >> Configuration Source: manually configured + >> Version Table Name: hello_bundle_migration_versions + >> Migrations Namespace: Application\HelloBundle\DoctrineMigrations + >> Migrations Directory: /path/to/symfony-sandbox/src/Bundle/HelloBundle/DoctrineMigrations + >> Current Version: 0 + >> Latest Version: 0 + >> Executed Migrations: 0 + >> Available Migrations: 0 + >> New Migrations: 0 + +Now, we can start working with migrations by generating a new blank migration +class: + +.. code-block:: bash + + $ php console doctrine:migrations:generate --bundle="Application\HelloBundle" + Generated new migration class to "/path/to/symfony-sandbox/src/Bundle/HelloBundle/DoctrineMigrations/Version20100621140655.php" + +Have a look at the newly generated migration class and you will see something +like the following:: + + namespace Application\HelloBundle\DoctrineMigrations; + + use Doctrine\DBAL\Migrations\AbstractMigration, + Doctrine\DBAL\Schema\Schema; + + class Version20100621140655 extends AbstractMigration + { + public function up(Schema $schema) + { + + } + + public function down(Schema $schema) + { + + } + } + +If you were to run the ``status`` command for the ``HelloBundle`` it will show +that you have one new migration to execute: + +.. code-block:: bash + + $ php console doctrine:migrations:status --bundle="Application\HelloBundle" + + == Configuration + + >> Name: HelloBundle Migrations + >> Configuration Source: manually configured + >> Version Table Name: hello_bundle_migration_versions + >> Migrations Namespace: Application\HelloBundle\DoctrineMigrations + >> Migrations Directory: /path/to/symfony-sandbox/src/Application/HelloBundle/DoctrineMigrations + >> Current Version: 0 + >> Latest Version: 2010-06-21 14:06:55 (20100621140655) + >> Executed Migrations: 0 + >> Available Migrations: 1 + >> New Migrations: 1 + + == Migration Versions + + >> 2010-06-21 14:06:55 (20100621140655) not migrated + +Now you can add some migration code to the ``up()`` and ``down()`` methods and migrate: + +.. code-block:: bash + + $ php console doctrine:migrations:migrate --bundle="Application\HelloBundle" + +.. _documentation: http://www.doctrine-project.org/projects/migrations/2.0/docs/en diff --git a/guides/pt_BR/doctrine/overview.rst b/guides/pt_BR/doctrine/overview.rst new file mode 100644 index 00000000000..c4301f8bb24 --- /dev/null +++ b/guides/pt_BR/doctrine/overview.rst @@ -0,0 +1,179 @@ +.. index:: + single: Doctrine + +Doctrine +======== + +The `Doctrine`_ project is the home of a selected set of PHP libraries +primarily focused on providing persistence services and related functionality. +The integration between Symfony2 and Doctrine2 implements most of the features +the project has to offer for working with relational databases, such as: + +* Database Abstraction Layer +* Object Relational Mapper +* Database Migrations + +.. tip:: + You can learn more about the `DBAL API`_ and `ORM API`_ on the official + Doctrine2 website. + +.. index:: + single: Doctrine; DBAL + +Doctrine DBAL +------------- + +The Doctrine Database Abstraction Layer (DBAL) offers an intuitive and +flexible API for communicating with the most popular relational databases that +exist today. In order to start using the DBAL, configure it: + +.. code-block:: yaml + + # config/config.yml + + doctrine.dbal: + driver: PDOMySql + dbname: Symfony2 + user: root + password: null + +Access the connection from your controllers by getting the +``database_connection`` service:: + + class UserController extends Controller + { + public function indexAction() + { + $conn = $this->container->getService('database_connection'); + + $users = $conn->fetchAll('SELECT * FROM users'); + } + } + +You can then execute a query and fetch the results as show above with the +``fetchAll()`` method. + +.. index:: + single: Doctrine; ORM + +Doctrine Object Relational Mapper +--------------------------------- + +The Doctrine Object Relational Mapper (ORM) is the prize library under the +Doctrine Project umbrella. It is built on top of the Doctrine DBAL (Database +Abstraction Layer) and offers transparent persistence of PHP5 objects to a +relational database. + +Before using the ORM, enable it in the configuration: + +.. code-block:: yaml + + # config/config.yml + + doctrine.orm: ~ + +Next, write your entity classes. A typical entity read as follows:: + + // Application/HelloBundle/Entities/User.php + + namespace Application\HelloBundle\Entities; + + /** + * @Entity + */ + class User + { + /** + * @Id + * @Column(type="integer") + * @GeneratedValue(strategy="IDENTITY") + */ + protected $id; + + /** + * @Column(type="string", length="255") + */ + protected $name; + + /** + * Get id + * + * @return integer $id + */ + public function getId() + { + return $this->id; + } + + /** + * Set name + * + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Get name + * + * @return string $name + */ + public function getName() + { + return $this->name; + } + } + +Now, create the schema by running the following command: + +.. code-block:: bash + + $ php hello/console doctrine:schema:create + +.. note:: + Don't forget to create the database if it does not exist yet. + +Eventually, use your entity and manage its persistent state with Doctrine:: + + use Application\HelloBundle\Entities\User; + + class UserController extends Controller + { + public function createAction() + { + $user = new User(); + $user->setName('Jonathan H. Wage'); + + $em = $this->container->getService('doctrine.orm.entity_manager'); + $em->persist($user); + $em->flush(); + + // ... + } + + public function editAction($id) + { + $em = $this->container->getService('doctrine.orm.entity_manager'); + $user = $em->createQuery('select u from HelloBundle:User where id = ?', $id); + $user->setBody('new body'); + $em->flush(); + + // ... + } + + public function deleteAction($id) + { + $em = $this->container->getService('doctrine.orm.entity_manager'); + $user = $em->createQuery('select e from HelloBundle:User where id = ?', $id); + $em->remove($user); + $em->flush(); + + // ... + } + } + +.. _Doctrine: http://www.doctrine-project.org/ +.. _DBAL API: http://www.doctrine-project.org/projects/dbal/2.0/docs/en +.. _ORM API: http://www.doctrine-project.org/projects/orm/2.0/docs/en diff --git a/guides/pt_BR/emails.rst b/guides/pt_BR/emails.rst new file mode 100644 index 00000000000..c54a010c0dd --- /dev/null +++ b/guides/pt_BR/emails.rst @@ -0,0 +1,37 @@ +.. index:: + single: Emails + +Emails +====== + +Sending emails with Symfony is a snap. First, enable the ``SwiftmailerBundle`` +and configure how you want them to be sent: + +.. code-block:: yaml + + # hello/config/config.yml + swift.mailer: + transport: gmail # can be any of smtp, mail, sendmail, or gmail + username: your_gmail_username + password: your_gmail_password + +Then, use the mailer from any action:: + + public function indexAction($name) + { + // get the mailer first (mandatory to initialize Swift Mailer) + $mailer = $this->getMailer(); + + $message = \Swift_Message::newInstance() + ->setSubject('Hello Email') + ->setFrom('send@example.com') + ->setTo('recipient@example.com') + ->setBody($this->renderView('HelloBundle:Hello:email', array('name' => $name))) + ; + $mailer->send($message); + + return $this->render(...); + } + +The email body is stored in a template, rendered with the ``renderView()`` +method. diff --git a/guides/pt_BR/event/index.rst b/guides/pt_BR/event/index.rst new file mode 100644 index 00000000000..da97d639e16 --- /dev/null +++ b/guides/pt_BR/event/index.rst @@ -0,0 +1,8 @@ +Event System +============ + +.. toctree:: + :maxdepth: 2 + + Overview + recipes diff --git a/guides/pt_BR/event/overview.rst b/guides/pt_BR/event/overview.rst new file mode 100644 index 00000000000..ac240506b3d --- /dev/null +++ b/guides/pt_BR/event/overview.rst @@ -0,0 +1,207 @@ +.. index:: + single: Event Dispatcher + +The Event Dispatcher +==================== + +Objected Oriented code has gone a long way to ensuring code extensibility. By +creating classes that have well defined responsibilities, your code becomes +more flexible as a developer can extend them with sub-classes to modify their +behaviors. But if he wants to share his changes with other developers who have +also made their own subclasses, code inheritance is moot. + +A real-world example is when you want to provide a plugin system for your +project. A plugin should be able to add methods, or do something before or +after a method is executed, without interfering with other plugins. This is +not an easy problem to solve with single inheritance, and multiple inheritance +(were it possible with PHP) has its own drawbacks. + +The Symfony2 Event Dispatcher implements the `Observer`_ pattern in a simple +and effective way to make all these things possible and make your projects +truly extensible (see the :doc:`recipes` section for some implementation +examples). + +The Event Dispatcher provides *dispatcher* that allows objects to communicate +together without knowing each others. Objects (*listeners*) can *connect* to +the dispatcher to listen to specific events, and some others can *notify* an +*event* to the dispatcher. Whenever an event is notified, the dispatcher will +call all the connected listeners. + +.. index:: + pair: Event Dispatcher; Naming Conventions + +Events +------ + +Unlike many other implementations of the Observer pattern, you don't need to +create a class to define a new event. All events are instead instances of the +``Event`` class and are uniquely identified by their names, a string that +optionally follows simple naming conventions: + +* use only lowercase letters, numbers, and underscores (``_``); + +* prefix names with a namespace followed by a dot (``.``); + +* use a verb to indicate what action will be done. + +Here are some examples of good event names: + +* change_culture +* response.filter_content + +.. note:: + You can of course extends the ``Event`` class to specialize an event further, or + enforce some constraints, but most of the time it adds an unnecessary level of + complexity. + +Besides its name, an ``Event`` instance can store additional data about the +notified event: + +* The *subject* of the event (most of the time, this is the object notifying + the event, but it can be any other object or ``null``); + +* The event name; + +* An array of parameters to pass to the listeners (an empty array by + default). + +These data are passed as arguments to the ``Event`` constructor:: + + use Symfony\Components\EventDispatcher\Event; + + $event = new Event($this, 'user.change_culture', array('culture' => $culture)); + +The event object has several methods to get the event data: + +* ``getName()``: Returns the event name; + +* ``getSubject()``: Gets the subject object attached to the event; + +* ``getParameters()``: Returns the event parameters. + +The event object can also be accessed as an array to get its +parameters:: + + echo $event['culture']; + +The Dispatcher +-------------- + +The dispatcher maintains a register of listeners and calls them whenever an +event is notified:: + + use Symfony\Components\EventDispatcher\EventDispatcher; + + $dispatcher = new EventDispatcher(); + +.. index:: + single: Event Dispatcher; Listeners + +Connecting Listeners +-------------------- + +Obviously, you need to connect some listeners to the dispatcher before it can +be useful. A call to the dispatcher ``connect()`` method associates a PHP +callable to an event:: + + $dispatcher->connect('user.change_culture', $callable); + +The ``connect()`` method takes two arguments: + +* The event name; + +* A PHP callable to call when the event is notified. + +.. note:: + A `PHP callable`_ is a PHP variable that can be used by the + ``call_user_func()`` function and returns ``true`` when passed to the + ``is_callable()`` function. It can be a ``\Closure`` instance, a string + representing a function, or an array representing an object method or a class + method. + +Once a listener is registered with the dispatcher, it waits until the event is +notified. For the above example, the dispatcher calls ``$callable`` whenever the +``user.change_culture`` event is notified; the listener receives an ``Event`` +instance as an argument. + +.. note:: + The listeners are called by the event dispatcher in the same order you + connected them. + +.. index:: + single: Event Dispatcher; Notification + +Notifying Events +---------------- + +Events can be notified by using three methods: + +* ``notify()`` + +* ``notifyUntil()`` + +* ``filter()`` + +``notify`` +~~~~~~~~~~ + +The ``notify()`` method notifies all listeners in turn:: + + $dispatcher->notify($event); + +By using the ``notify()`` method, you make sure that all the listeners +registered for the event are executed but their return values is ignored. + +``notifyUntil`` +~~~~~~~~~~~~~~~ + +In some cases, you need to allow a listener to stop the event and prevent +further listeners from being notified about it. In this case, you should use +``notifyUntil()`` instead of ``notify()``. The dispatcher will then execute all +listeners until one returns ``true``, and then stop the event notification:: + + $dispatcher->notifyUntil($event); + +The listener that stops the chain may also call the ``setReturnValue()`` method +to return back some value to the subject:: + + $event->setReturnValue('foo'); + + return true; + +The notifier can check if a listener has processed the event by calling the +``isProcessed()`` method:: + + if ($event->isProcessed()) { + $ret = $event->getReturnValue(); + + // ... + } + +``filter`` +~~~~~~~~~~ + +The ``filter()`` method asks all listeners to filter a given value, passed by +the notifier as its second argument, and retrieved by the listener callable as +the second argument:: + + $dispatcher->filter($event, $response->getContent()); + + $listener = function (Event $event, $content) + { + // do something with $content + + // don't forget to return the content + return $content; + }; + +All listeners are passed the value and they must return the filtered value, +whether they altered it or not. All listeners are guaranteed to be executed. + +The notifier can get the filtered value by calling the ``getReturnValue()`` +method:: + + $ret = $event->getReturnValue(); + +.. _Observer: http://en.wikipedia.org/wiki/Observer_pattern +.. _PHP callable: http://www.php.net/manual/en/function.is-callable.php diff --git a/guides/pt_BR/event/recipes.rst b/guides/pt_BR/event/recipes.rst new file mode 100644 index 00000000000..139d7213a82 --- /dev/null +++ b/guides/pt_BR/event/recipes.rst @@ -0,0 +1,175 @@ +.. index:: + single: Event Dispatcher; Recipes + +Event Dispatcher Recipes +======================== + +Passing along the Event Dispatcher Object +----------------------------------------- + +If you have a look at the ``EventDispatcher`` class, you will notice that the +class does not act as a Singleton (there is no ``getInstance()`` static method). +That is intentional, as you might want to have several concurrent event +dispatchers in a single PHP request. But it also means that you need a way to +pass the dispatcher to the objects that need to connect or notify events. + +The best practice is to inject the event dispatcher object into your objects, +aka dependency injection. + +You can use constructor injection:: + + class Foo + { + protected $dispatcher = null; + + public function __construct(EventDispatcher $dispatcher) + { + $this->dispatcher = $dispatcher; + } + } + +Or setter injection:: + + class Foo + { + protected $dispatcher = null; + + public function setEventDispatcher(EventDispatcher $dispatcher) + { + $this->dispatcher = $dispatcher; + } + } + +Choosing between the two is really a matter of taste. I tend to prefer the +constructor injection as the objects are fully initialized at construction +time. But when you have a long list of dependencies, using setter injection +can be the way to go, especially for optional dependencies. + +.. tip:: + If you use dependency injection like we did in the two examples above, you can + then easily use the Symfony Dependency Injection Component to elegantly manage + these objects. + +Doing something before or after a Method Call +--------------------------------------------- + +If you want to do something just before, or just after a method is called, you +can notify respectively an event at the beginning or at the end of the method:: + + class Foo + { + // ... + + public function send($foo, $bar) + { + // do something before the method + $event = new Event($this, 'foo.do_before_send', array('foo' => $foo, 'bar' => $bar)); + $this->dispatcher->notify($event); + + // the real method implementation is here + // $ret = ...; + + // do something after the method + $event = new Event($this, 'foo.do_after_send', array('ret' => $ret)); + $this->dispatcher->notify($event); + + return $ret; + } + } + +Adding Methods to a Class +------------------------- + +To allow multiple classes to add methods to another one, you can define the +magic ``__call()`` method in the class you want to be extended like this:: + + class Foo + { + // ... + + public function __call($method, $arguments) + { + // create an event named 'foo.method_is_not_found' + // and pass the method name and the arguments passed to this method + $event = new Event($this, 'foo.method_is_not_found', array('method' => $method, 'arguments' => $arguments)); + + // calls all listeners until one is able to implement the $method + $this->dispatcher->notifyUntil($event); + + // no listener was able to proces the event? The method does not exist + if (!$event->isProcessed()) { + throw new \Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method)); + } + + // return the listener returned value + return $event->getReturnValue(); + } + } + +Then, create a class that will host the listener:: + + class Bar + { + public function addBarMethodToFoo(Event $event) + { + // we only want to respond to the calls to the 'bar' method + if ('bar' != $event['method']) { + // allow another listener to take care of this unknown method + return false; + } + + // the subject object (the foo instance) + $foo = $event->getSubject(); + + // the bar method arguments + $arguments = $event['parameters']; + + // do something + // ... + + // set the return value + $event->setReturnValue($someValue); + + // tell the world that you have processed the event + return true; + } + } + +Eventually, add the new ``bar`` method to the ``Foo`` class:: + + $dispatcher->connect('foo.method_is_not_found', array($bar, 'addBarMethodToFoo')); + +Modifying Arguments +------------------- + +If you want to allow third party classes to modify arguments passed to a +method just before that method is executed, add a ``filter`` event at the +beginning of the method:: + + class Foo + { + // ... + + public function render($template, $arguments = array()) + { + // filter the arguments + $event = new Event($this, 'foo.filter_arguments'); + $this->dispatcher->filter($event, $arguments); + + // get the filtered arguments + $arguments = $event->getReturnValue(); + // the method starts here + } + } + +And here is a filter example:: + + class Bar + { + public function filterFooArguments(Event $event, $arguments) + { + $arguments['processed'] = true; + + return $arguments; + } + } diff --git a/guides/pt_BR/forms.rst b/guides/pt_BR/forms.rst new file mode 100644 index 00000000000..787d0e981d3 --- /dev/null +++ b/guides/pt_BR/forms.rst @@ -0,0 +1,353 @@ +.. index:: + single: Forms + +Forms +===== + +Symfony2 features a sophisticated Form component that allows you to easily +create mighty HTML forms. + +Your First Form +--------------- + +A form in Symfony2 is a transparent layer on top of your domain model. It +reads properties from an object, displays the values in the form and allows +the user to change them. When the form is submitted, the values are written +back into the object. + +Let's see how this works in a practical example. Let's create a simple +``Customer`` class:: + + class Customer + { + public $name; + + private $age = 20; + + public function getAge() { + return $this->age; + } + + public function setAge($age) { + $this->age = $age; + } + } + +The class contains two properties ``name`` and "age". The property ``$name`` is +public, while ``$age`` can only be modified through setters and getters. + +Now let's create a form to let the visitor fill the data of the object:: + + // src/Application/HelloBundle/Controller/HelloController.php + public function signupAction() + { + $customer = new Customer(); + + $form = new Form('customer', $customer, $this->container->getValidatorService()); + $form->add(new TextField('name')); + $form->add(new IntegerField('age')); + + return $this->render('HelloBundle:Hello:signup', array('form' => $form)); + } + +A form consists of various fields. Each field represents a property in your +class. The property must have the same name as the field and must either be +public or accessible through public getters and setters. Now let's create a +simple template to render the form: + +.. code-block:: html+php + + # src/Application/HelloBundle/Resources/views/Hello/signup.php + extend('HelloBundle::layout') ?> + + renderFormTag('#') ?> + renderErrors() ?> + render() ?> + + + +When the user submits the form, we also need to handle the submitted data. +All the data is stored in a POST parameter with the name of the form:: + + # src/Application/HelloBundle/Controller/HelloController.php + public function signupAction() + { + $customer = new Customer(); + $form = new Form('customer', $customer, $this->container->getValidatorService()); + + // form setup... + + if ($this->getRequest()->getMethod() == 'POST') + { + $form->bind($this->getRequest()->getParameter('customer')); + + if ($form->isValid()) + { + // save $customer object and redirect + } + } + + return $this->render('HelloBundle:Hello:signup', array('form' => $form)); + } + +Congratulations! You just created your first fully-functional form with +Symfony2. + +.. index:: + single: Forms; Fields + +Form Fields +----------- + +As you have learned, a form consists of one or more form fields. In Symfony2, +form fields have two responsibilities: + +* Render HTML +* Convert data between normalized and humane representations + +Let's look at the ``DateField`` for example. While you probably prefer to store +dates as strings or ``DateTime`` objects, users rather like to choose them from a +list of drop downs. ``DateField`` handles the rendering and type conversion for you. + +Basic Fields +~~~~~~~~~~~~ + +Symfony2 ships with all fields available in plain HTML: + +============= ================== +Field Name Description +============= ================== +TextField An input tag for entering short text +TextareaField A textarea tag for entering long text +CheckboxField A checkbox +ChoiceField A drop-down or multiple radio-buttons/checkboxes for selecting values +PasswordField A password input tag +HiddenField A hidden input tag +============= ================== + +Localized Fields +~~~~~~~~~~~~~~~~ + +The Form component also features fields that render differently depending on +the locale of the user: + +============= ================== +Field Name Description +============= ================== +NumberField A text field for entering numbers +IntegerField A text field for entering integers +PercentField A text field for entering percent values +MoneyField A text field for entering money values +DateField A text field or multiple drop-downs for entering dates +BirthdayField An extension of DateField for selecting birthdays +TimeField A text field or multiple drop-downs for entering a time +DateTimeField A combination of DateField and TimeField +TimezoneField An extension of ChoiceField for selecting a timezone +============= ================== + +Field Groups +~~~~~~~~~~~~ + +Field groups allow you to combine multiple fields together. While normal fields +only allow you to edit scalar data types, field groups can be used to edit +whole objects or arrays. Let's add a new class ``Address`` to our model:: + + class Address + { + public $street; + public $zipCode; + } + +Now we can add a property ``$address`` to the customer that stores one ``Address`` +object:: + + class Customer + { + // other properties ... + + public $address; + } + +We can use a field group to show fields for the customer and the nested address +at the same time:: + + # src/Application/HelloBundle/Controller/HelloController.php + public function signupAction() + { + $customer = new Customer(); + $customer->address = new Address(); + + // form configuration ... + + $group = new FieldGroup('address'); + $group->add(new TextField('street')); + $group->add(new TextField('zipCode')); + $form->add($group); + + // process form ... + } + +With only these little changes you can now edit also the ``Address`` object! +Cool, ey? + +Repeated Fields +~~~~~~~~~~~~~~~ + +The ``RepeatedField`` is an extended field group that allows you to output a field +twice. The repeated field will only validate if the user enters the same value +in both fields:: + + $form->add(new RepeatedField(new TextField('email'))); + +This is a very useful field for querying email addresses or passwords! + +Collection Fields +~~~~~~~~~~~~~~~~~ + +The ``CollectionField`` is a special field group for manipulating arrays or +objects that implement the interface ``Traversable``. To demonstrate this, we +will extend the ``Customer`` class to store three email addresses:: + + class Customer + { + // other properties ... + + public $emails = array('', '', ''); + } + +We will now add a ``CollectionField`` to manipulate these addresses:: + + $form->add(new CollectionField(new TextField('emails'))); + +If you set the option "modifiable" to ``true``, you can even add or remove rows +in the collection via Javascript! The ``CollectionField`` will notice it and +resize the underlying array accordingly. + +.. index:: + single: Forms; Validation + +Form Validation +--------------- + +You have already learned in the last part of this tutorial how to set up +validation constraints for a PHP class. The nice thing is that this is enough +to validate a Form! Remember that a form is nothing more than a gateway for +changing data in an object. + +What now if there are further validation constraints for a specific form, that +are irrelevant for the underlying class? What if the form contains fields that +should not be written into the object? + +The answer to that question is most of the time to extend your domain model. +We'll demonstrate this approach by extending our form with a checkbox for +accepting terms and conditions. + +Let's create a simple ``Registration`` class for this purpose:: + + class Registration + { + /** @Validation({ @Valid }) */ + public $customer; + + /** @Validation({ @AssertTrue(message="Please accept the terms and conditions") }) */ + public $termsAccepted = false; + + public process() + { + // save user, send emails etc. + } + } + +Now we can easily adapt the form in the controller:: + + # src/Application/HelloBundle/Controller/HelloController.php + public function signupAction() + { + $registration = new Registration(); + $registration->customer = new Customer(); + + $form = new Form('registration', $registration, $this->container->getValidatorService()); + $form->add(new CheckboxField('termsAccepted')); + + $group = new FieldGroup('customer'); + + // add customer fields to this group ... + + $form->add($group); + + if ($this->getRequest()->getMethod() == 'POST') + { + $form->bind($this->getRequest()->getParameter('customer')); + + if ($form->isValid()) + { + $registration->process(); + } + } + + return $this->render('HelloBundle:Hello:signup', array('form' => $form)); + } + +The big benefit of this refactoring is that we can reuse the ``Registration`` +class. Extending the application to allow users to sign up via XML is no +problem at all! + +.. index:: + single: Forms; View + +Customizing the View +-------------------- + +Unfortunately the output of ``$form->render()`` doesn't look too great. Symfony +2.0 makes it very easy though to customize the HTML of a form. You can access +every field and field group in the form by its name. All fields offer the +method ``render()`` for rendering the widget and ``renderErrors()`` for rendering +a ``