The UserInterface Methods (Keep some Blank!)

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Our job: fill in the 5 methods from UserInterface. But check this out - I'm going rogue - I'm only going to implement 2 of them: getUsername() and getRoles().

getUsername()

First getUsername()... is super-unimportant. Just return any unique user string you want - a username, an email, a uuid, a funny, but unique joke - whatever. This is only used to show you who is logged in when you're debugging.

In our app, our users won't have a username - but they will have an email. Add a private $email property then just use that in getUsername(): return $this->email:

... lines 1 - 7
class User implements UserInterface
{
private $email;
// needed by the security system
public function getUsername()
{
return $this->email;
}
... lines 17 - 46
}

And add a setter for that method:

... lines 1 - 7
class User implements UserInterface
{
... lines 10 - 42
public function setEmail($email)
{
$this->email = $email;
}
}

We'll eventually need that to create users.

getRoles()

Cool: half-way done! Now: getRoles(). We'll talk roles later with authorization, but these are basically permissions we want to give the user. For now, give every user the same, one role: ROLE_USER:

... lines 1 - 7
class User implements UserInterface
{
... lines 10 - 16
public function getRoles()
{
return ['ROLE_USER'];
}
... lines 21 - 40
}

What about getPassword(), getSalt() and eraseCredentials()?

So what about getPassword(), getSalt() and eraseCredentials()? Keep them blank:

... lines 1 - 7
class User implements UserInterface
{
... lines 10 - 21
public function getPassword()
{
// leaving blank - I don't need/have a password!
}
public function getSalt()
{
// leaving blank - I don't need/have a password!
}
public function eraseCredentials()
{
// leaving blank - I don't need/have a password!
}
... lines 36 - 40
}

Whaat? It turns out, you only need these if your app is personally responsible for storing and checking user passwords. In our app - to start - we're not going to have passwords: we're just going to let anyone login with a single, central, hardcoded password. But there are also real-world situations where your app isn't responsible for managing and checking passwords.

If you have one of these, feel ok leaving these blank.

Setting up the User Entity

So in our application, we want to store users in the database. So let's set this class up with Doctrine. Copy the use statement from SubFamily and paste it here:

... lines 1 - 6
use Doctrine\ORM\Mapping as ORM;
... lines 8 - 63

Next, I'll put my cursor inside the class, press Command+N - or use the "Code"->"Generate" menu - and select "ORM class":

... lines 1 - 8
/**
* @ORM\Entity
* @ORM\Table(name="user")
*/
class User implements UserInterface
... lines 14 - 63

Next, add a private $id property and press Command+N one more time. This time, choose "ORM annotation" and highlight both fields:

... lines 1 - 12
class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", unique=true)
*/
private $email;
... lines 26 - 61
}

Oh, and while we're here, let's add unique=true for the email field:

... lines 1 - 12
class User implements UserInterface
{
... lines 15 - 21
/**
* @ORM\Column(type="string", unique=true)
*/
private $email;
... lines 26 - 61
}

Perfect: we have a fully functional User class. Sure, it only has an id and email, but that's enough!

Since we just added a new entity, let's generate a migration:

./bin/console doctrine:migrations:diff

I'll copy the class and open it up quickly, just to make sure it looks right:

... lines 1 - 10
class Version20160524105319 extends AbstractMigration
{
... lines 13 - 15
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
}
... lines 23 - 26
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('DROP TABLE user');
}
}

Looks great!

Adding User Fixtures

Finally, let's add some users to the database. Open our trusty fixtures.yml. I'll copy the SubFamily section, change the class to User and give these keys user_1..10:

... lines 1 - 22
AppBundle\Entity\User:
user_{1..10}:
... lines 25 - 26

Then, the only field is email. Set it to weaverryan+<current()>@gmail.com:

... lines 1 - 22
AppBundle\Entity\User:
user_{1..10}:
email: weaverryan+<current()>@gmail.com

The current() function will return 1 through 10 as Alice loops through our set. That'll give us weaverryan+1@gmail.com up to weaverryan+10@gmail.com. And if you didn't know, Gmail ignores everything after a + sign, so these will all be delivered to weaverryan@gmail.com. There's your Internet hack for the day.

Ok, let's roll! Don't forget to run the migration first:

./bin/console doctrine:migrations:migrate

And then load the fixtures:

./bin/console doctrine:fixtures:load

Hey hey: you've got users in the database. Let's let 'em login.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}