Setup Testing and Fixtures in Symfony2: The Easy Way
Setting up testing and fixtures in Symfony2 is vital if you plan on starting Test Driven Development or you simply want to start covering your code with properly written tests that can access mock data.
1. Install PHPUnit and php 5.6
The first thing you need to do is to install PHPUnit on your machine:
$ wget https://phar.phpunit.de/phpunit.phar $ chmod +x phpunit.phar $ sudo mv phpunit.phar /usr/local/bin/phpunit $ phpunit --version
and then, if needed, also upgrade your PHP version to 5.6:
$ sudo apt-get install language-pack-en-base $ export LC_CTYPE="en_US.UTF-8" $ sudo add-apt-repository ppa:ondrej/php5-5.6 $ sudo apt-get update $ sudo apt-get install php5
and make sure everything's ok by running: phpunit -c app/
Please note that this is not a testing tutorial so if you'd like to learn more about how to actually test your Symfony2 app, then please read their documentation.
2. Setup and create a test database
In order to be able to configure testing and fixtures in a Symfony2 app a separate, independent database is needed so that your dev environment is not affected.
In the config_test.yml
file you simply need to add:
doctrine: dbal: host: 127.0.0.1 dbname: testdb user: [YOUR_MYSQL_USERNAME] password: [YOUR_MYSQL_PASSWORD]
then simply run php app/console doctrine:database:create --env=test
Related: Send Emails in Symfony2: The Right Way
3. Build the BaseTestSetup class
Once our test database is ready we can start building our BaseTestSetup
class, one which will serve as parent for all of our tests.
<?php namespace AppBundle\Tests; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; abstract class BaseTestSetup extends WebTestCase { protected $client; protected $container; protected $em; protected function setUp() { $this->client = static::createClient(); $this->container = $this->client->getContainer(); $this->em = static::$kernel->getContainer() ->get('doctrine') ->getManager(); } }
4. Install the LiipFunctionalTestBundle
Even though Symfony does have an out of the box solution to setup and test your app, the LiipFunctionalTestBundle provides base classes for functional tests to assist in setting up testing and fixtures and HTML5 validation.
After you install and configure the bundle, go back to your BaseTestSetup
class and make the necessary modifications:
<?php namespace AppBundle\Tests; use Doctrine\ORM\Tools\SchemaTool; use Liip\FunctionalTestBundle\Test\WebTestCase; abstract class BaseTestSetup extends WebTestCase { protected $client; protected $container; protected $em; protected function setUp() { $this->client = static::createClient(); $this->container = $this->client->getContainer(); $this->em = static::$kernel->getContainer() ->get('doctrine') ->getManager(); if (!isset($metadatas)) { $metadatas = $this->em->getMetadataFactory()->getAllMetadata(); } $schemaTool = new SchemaTool($this->em); $schemaTool->dropDatabase(); if (!empty($metadatas)) { $schemaTool->createSchema($metadatas); } $this->postFixtureSetup(); $this->loadFixtures(array( 'AppBundle\DataFixtures\ORM\LoadUserData', )); } }
The new code above simply drops and creates the database each them tests run. Then it loads the fixtures that you'll need. The LoadUserData
class does not exist yet so we'll go right ahead and add it by following the handy tutorial found on Symfony's website.
5. Write the very first test
Now that have your fixtures ready, you can go ahead and write your first test. Create a new file in your AppBundle/Tests
folder called UserTest.php
, next to BaseTestSetup.php
:
<?php namespace AppBundle\Tests; use AppBundle\Tests\BaseTestSetup; class UserTest extends BaseTestSetup { public function testSuggestImprovementEmail() { // assuming that you named your user 'username' in your Fixtures $crawler = $this->client->request('GET', '/show/username'); $this->assertGreaterThan( 0, $crawler->filter('html:contains("Hello first user!")')->count() ); } }
And that's about it! You now have everything you need to properly test your awesome app.
Send Emails in Symfony2: The Right Way
No matter what your app is about, at one you point you will have to send emails in Symfony2. Even though this process might seem straightforward and easy to apply, it can actually get pretty tricky. Here's how you can send emails in Symfony2 by properly splitting your functionality and templates.
1. Mailer parameters
First off, you're going to need to add some email data into your parameters.yml
file that will tell Swift Mailer which SMTP server to use. At this point, it would be advised to create an account at an email sending service. For this short tutorial, we're going to use SendGrid.
mailer_transport: smtp mailer_host: smtp.sendgrid.net mailer_user: your_sendgrid_username mailer_password: 'your_sendgrid_password' mailer_port: 587 contact_email: contact@yourapp.com from_email: hello@yourapp.com from_name: YourApp
As you can see, we've also added three parameters that will help us to send emails in Symfony2: contact email, from email and from name.
2. Send emails in Symfony2 using a MailManager
Having a MailManager is helpful because the code needed to send emails will only reside in a single place. Thus, it will be way easier to maintain. Here's how you should build the MailManager class:
<?php namespace AppBundle\Lib; class MailManager { protected $mailer; protected $twig; public function __construct(\Swift_Mailer $mailer, \Twig_Environment $twig) { $this->mailer = $mailer; $this->twig = $twig; } /** * Send email * * @param string $template email template * @param mixed $parameters custom params for template * @param string $to to email address or array of email addresses * @param string $from from email address * @param string $fromName from name * * @return boolean send status */ public function sendEmail($template, $parameters, $to, $from, $fromName = null) { $template = $this->twig->loadTemplate('AppBundle:Mail:' . $template . '.html.twig'); $subject = $template->renderBlock('subject', $parameters); $bodyHtml = $template->renderBlock('body_html', $parameters); $bodyText = $template->renderBlock('body_text', $parameters); try { $message = \Swift_Message::newInstance() ->setSubject($subject) ->setFrom($from, $fromName) ->setTo($to) ->setBody($bodyHtml, 'text/html') ->addPart($bodyText, 'text/plain') ; $response = $this->mailer->send($message); } catch (\Exception $ex) { return $ex->getMessage(); } return $response; } }
In the sendEmail
function, you can easily define a $template
variable which will hold whatever template you need, depending on the type of email. You'll see the MailManager
in action in the 4th and last section of this short tutorial, where you'll use it to send a contact email.
Oh, and don't forget to register the service in services.yml
:
mail_manager: class: AppBundle\Lib\MailManager arguments: ["@mailer", "@twig"]
Related: Symfony2 Facebook and Google Login: The Easy Way
3. Define a simple template
In this section you'll be building the three main blocks any email should have (subject, html and text) and store them in a template which you'll extend as needed.
{# src/AppBundle/Mail/template.html.twig #} {% block subject %} default subject {% endblock %} {% block body_text %} default text {% endblock %} {% block body_html %} <p>default body</p> {% endblock %}
4. A sample email sending action
Now that you have everything set, you can go ahead and build your first mail sending action. As an example, here's how a contact action should look like. It will send emails to the email address defined in parameters.yml
.
public function contactAction(Request $request) { $form = $this->createForm(new ContactType()); $form->handleRequest($request); //this is where we define which template to use $template = 'contact'; if($form->isValid()){ //Get data from the submitted form $data = $form->getData(); $mail_params = array( 'firstName' => $data["firstName"], 'lastName' => $data["lastName"], 'message' => $data["message"], 'phoneNumber' => $data["phoneNumber"], 'email' => $data["email"] ); //grab the addresses defined in parameters.yml $to = $this->container->getParameter('contact_email'); $from = $this->container->getParameter('from_email'); $fromName = $this->container->getParameter('from_name'); //use the MailManager service to send emails $message = $this->container->get('mail_manager'); $message->sendEmail($template, $mail_params, $to, $from, $fromName); return $this->redirectToRoute('contact'); } return $this->render('AppBundle:StaticPages:contact.html.twig',array( 'form' => $form->createView() )); }
Since you'll be using the contact
email body, you will need to build it by extending the template defined at step #3:
{% extends "AppBundle:Mail:template.html.twig" %} {% block subject %} YourApp Contact Message {% endblock %} {% block body_text %} {% autoescape false %} Name: {{ firstName }} {{ lastName }} Message: {{ message }} Phone Number: {{ phoneNumber }} Email address: {{ email }} {% endautoescape %} {% endblock %} {% block body_html %} {% autoescape false %} <p>Name: {{ firstName }} {{ lastName }}</p> <p>Message: {{ message }}</p> <p>Phone Number: {{ phoneNumber }}</p> <p>Email address: {{ email }}</p> {% endautoescape %} {% endblock %}
And there you have it - an easy way to build and maintain your emails. If you know of any other easier system used to send emails in Symfony2, please let me know in the comment section below.
Symfony2 Facebook and Google Login: The Easy Way
Since pretty much any potential user of an app has a Facebook or a Google account, it's crucial to have a login or registration service using these social networks. Luckily, HWIOAuthBundle provides the necessary tools to accomplish this. So here's an easy and straightforward way to integrate HWIOAuth in our app alongside FOSUserBundle:
1. Configure HWIOauthBundle
Assuming that you've already installed and configured FOSUserBundle, we now need to add HWIOAuthBundle in our application. As mentioned in their setup page, we simply have to:
composer require hwi/oauth-bundle
Then we should enable the bundle in the kernel:
// app/AppKernel.php public function registerBundles() { $bundles = array( // ... new HWI\Bundle\OAuthBundle\HWIOAuthBundle(), ); }
and then we can import the social routes:
# app/config/routing.yml hwi_oauth_redirect: resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml" prefix: /connect hwi_oauth_login: resource: "@HWIOAuthBundle/Resources/config/routing/login.xml" prefix: /login
Once we have these setup, we can start configuring the security details in security.yml
:
firewalls: main: pattern: ^/ oauth: failure_path: your_path login_path: your_path check_path: /connect_check provider: fos_userbundle resource_owners: facebook: "/login/check-facebook" google: "/login/check-google" oauth_user_provider: service: app.provider.oauth
And then we can finally add Facebook and Google into HWIOAuth's configuration in config.yml
:
hwi_oauth: # name of the firewall in which this bundle is active, this setting MUST be set firewall_name: main connect: account_connector: app.provider.oauth resource_owners: facebook: type: facebook client_id: %facebook_client_id% client_secret: %facebook_client_secret% scope: "email, public_profile" infos_url: "https://graph.facebook.com/me?fields=id,name,email,picture.type(large)" paths: email: email options: display: page google: type: google client_id: %google_client_id% client_secret: %google_client_secret% scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile" paths: email: email
2. Build our own user provider by extending HWIOAuth's FOSUBUserProvider
As you've probably noticed, we used app.provider.oauth
in both security.yml
and config.yml
. So we need to define that service in services.yml
:
app.provider.oauth: class: AppBundle\Security\Core\User\OAuthUserProvider arguments: [@fos_user.user_manager,{facebook: facebookID, google: googleID}]
And then build it in our AppBundle:
<?php namespace AppBundle\Security\Core\User; use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface; use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserChecker; use Symfony\Component\Security\Core\User\UserInterface; /** * Class OAuthUserProvider * @package AppBundle\Security\Core\User */ class OAuthUserProvider extends BaseClass { /** * {@inheritdoc} */ public function loadUserByOAuthUserResponse(UserResponseInterface $response) { $socialID = $response->getUsername(); $user = $this->userManager->findUserBy(array($this->getProperty($response)=>$socialID)); $email = $response->getEmail(); //check if the user already has the corresponding social account if (null === $user) { //check if the user has a normal account $user = $this->userManager->findUserByEmail($email); if (null === $user || !$user instanceof UserInterface) { //if the user does not have a normal account, set it up: $user = $this->userManager->createUser(); $user->setEmail($email); $user->setPlainPassword(md5(uniqid())); $user->setEnabled(true); } //then set its corresponding social id $service = $response->getResourceOwner()->getName(); switch ($service) { case 'google': $user->setGoogleID($socialID); break; case 'facebook': $user->setFacebookID($socialID); break; } $this->userManager->updateUser($user); } else { //and then login the user $checker = new UserChecker(); $checker->checkPreAuth($user); } return $user; } }
Related: What Nobody Tells You When You're a Junior Developer
3. Override the User Model
In our user provider defined above we modified two attributes that do not exist in FOSUser's User model: $facebookId
and $googleId
. So we need to override the User model and add them.
namespace AppBundle\Entity; use FOS\UserBundle\Model\User as BaseUser; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository") * @ORM\Table(name="fos_user") */ class User extends BaseUser { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @var string * * @ORM\Column(name="facebook_id", type="string", nullable=true) */ private $facebookID; /** * @var string * * @ORM\Column(name="google_id", type="string", nullable=true) */ private $googleID; // ... }
Don't forget to update your database and then you can move forward to the next step.
4. Add Facebook and Google to our login and register pages
Now we're all set to update our Twig files with HWIOauth's paths. Since we're using FOSUser, we've overridden their register.html.twig
and login.html.twig
files and then added the social links:
{# ... #} <a href="{{ path('hwi_oauth_service_redirect', {'service': 'facebook' }) }}"> <span>Facebook</span> </a> <a href="{{ path('hwi_oauth_service_redirect', {'service': 'google' }) }}"> <span>Google</span> </a> {# ... #}
And there you have it: a fully functional Facebook and Google login and signup system. Now we can go to Facebook Developers and Google Developers Console, create our apps and add our client_id
and client_secret
in config.yml
. Have fun!
If you have a different take on this process and can make it even easier, please let us know in the comment section below.
What Nobody Tells You When You're a Junior Developer
Hooray! You've landed your dream job and you can now proudly call yourself a Junior Developer. The future looks nothing but bright and your awesome life story is underway. But now the hard part actually begins. Here's how you can stay on top of things with some useful tips I wish I knew when I was in your shoes as a junior developer.
An Introduction to Artificial Intelligence: Facts and Fallacies
One way or another, we all heard of Artificial Intelligence. It's been there since we were born and it will surely outlive us. It’s a promise of a Utopian future and could be a harbinger of our own demise. It could help us end poverty, disease and pollution or it could see us a threat and decide to take us out. Whatever the future might hold, one thing's certain: Artificial Intelligence is (or will be) the Pyramids of a generation and most likely mankind's greatest creation.
How to Show the Full Path in the OS X Terminal
Working and constantly switching between multiple projects in the OS X Terminal or iTerm (whatever you prefer) can be hassle since, by default, you cannot see the full path of the directory you're working in. Here's how to change that.
But first, in the spirit of full disclosure, I'd like to mention that I only found out how to display the full path after I accidentally destroyed the virtual machine of the wrong project and I had to install the whole damn thing all over again.
As you might have noticed, the Terminal only displays the current folder in which you're working so it can be pretty easy to make a mistake when your projects have a similar folder structure or are built with the same framework.
This first thing you need to do to address that is to fire up your Terminal and open your .bash_profile
file:
sudo nano ~/.bash_profile
Then head over to a new line and add:
PS1="\w$ "
Save and close the file, then restart your Terminal. You should now notice that when you browse through your directories, the path should look something like this:
~/Development/hpm/vagrant$
The \w
symbol you noticed stands for "Current working directory, full path (ie: /Users/Admin/Desktop)" while the $
mark followed by a space is simply a separator between the working path and the input area.
If you want to make the Terminal look even more awesome, here are some other options you can add to PS1:
\W – Current working directory (ie: Desktop/) \d – Current date \t – Current time \h – Host name \# – Command number \u – User name
So for, example, using PS1="\u@\w$ "
will output "Sebi@~/Development$ "
.
I really hope this small tutorial helped. Have fun!
Who Really Invented the Internet?
While some see it as one of the greatest evolutionary steps in the history of mankind, most of us still use it for cat videos and annoying status updates. Nonetheless, the Internet has clearly taken over our lives, being directly present in our homes, our pockets, our workplace and soon enough, even in our brains. But who actually built the damn thing?
Should You Do Scrum?
"It is not the strongest of
the speciesteams that survives, nor the most intelligent. It is the one that is most adaptable to change."
That should be a good enough reason for all teams out there to adopt Scrum, from wedding planners to construction companies. But that isn't always the case and if you stick around, you're going to find out why.
iOS vs. Android - Which One Should You Choose?
Six users, six questions. After a (very) long research about which mobile OS is better between the two, I realized that the simplest way to find that out is by actually talking to people that use them. As you might expect, everyone has its own requirements and tastes, but in the end, one of the two OSs clearly stands out.
What Are Transactional Emails?
Every now and then, you might come across a technical term that's not that self-explanatory. One such term is a 'transactional email'. Although few people outside of the email business actually know what that stands for, it's one of the most important aspects of every business that generates its revenue on the Internet.