Skip to content
Open
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e28b52b
Added support for Laravel Migrations
top-webmaster Dec 6, 2014
96aa6ea
Removed commented out binding
top-webmaster Dec 6, 2014
5a5299f
Fixed getRan() method
top-webmaster Dec 6, 2014
18e6419
Added password reminders
top-webmaster Dec 11, 2014
a783068
Fixed migrations and modified dependencies.
top-webmaster Dec 12, 2014
ea80055
Modified versioning
top-webmaster Dec 13, 2014
8fb46ef
Merge commit '5bf7684fb71c402a7989a04e2a3dd72b47f4cb30'
top-webmaster Jan 30, 2015
6e4069b
Merge branch 'master' of https://github.com/mitchellvanw/laravel-doct…
top-webmaster Jan 30, 2015
e7bce84
Added really convoluted lazy loading of Doctrine for the `DoctrineMig…
top-webmaster Jan 31, 2015
7ca4ceb
Merged branch 'upstream/master'
top-webmaster Jun 13, 2015
5a7255f
Added tests for `DoctrineMigrationRepository`. Started to upgrade to …
top-webmaster Jun 13, 2015
1b8137a
Updated service provider
top-webmaster Jun 13, 2015
a30d319
Fixed bug
top-webmaster Jun 14, 2015
c456af2
Added lazy loading of the Doctrine EntityManager within the `Doctrine…
top-webmaster Jun 14, 2015
f7c3be8
Hopefully fixed lazy loading
top-webmaster Jun 14, 2015
27fef32
Updated user provider
top-webmaster Jun 14, 2015
531f71c
Updated metadata path for Laravel 5
top-webmaster Jun 21, 2015
8bfea99
Updated for Laravel 5
top-webmaster Jun 22, 2015
8bd656e
More bugfixes
top-webmaster Jun 23, 2015
702e3e8
Fixed publish path
top-webmaster Jun 26, 2015
937e850
Updated password resets
top-webmaster Jun 26, 2015
989aecd
Updated entity name
top-webmaster Jun 26, 2015
8bb6a6a
Fixed password reminders
top-webmaster Jun 26, 2015
8c8487e
Fixed proxy generation
top-webmaster Jul 6, 2015
6bceaae
Added extra binding
top-webmaster Jul 7, 2015
1c5e82c
Moved entity registration into `PasswordResetServiceProvider`.
top-webmaster Sep 30, 2015
bfbd0cd
Stopped deferring this provider because it writes to the config
top-webmaster Oct 1, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
[![License](https://poser.pugx.org/mitchellvanw/laravel-doctrine/license.png)](https://packagist.org/packages/mitchellvanw/laravel-doctrine)
[![Total Downloads](https://poser.pugx.org/mitchellvanw/laravel-doctrine/downloads.png)](https://packagist.org/packages/mitchellvanw/laravel-doctrine)

A Doctrine 2 implementation that melts with Laravel 4.
A Doctrine 2 implementation that melts with Laravel 5.

## Documentation

7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -12,9 +12,10 @@
],
"require": {
"php": ">=5.5.0",
"illuminate/support": "4.*|5.*",
"doctrine/orm": "2.5.*",
"doctrine/migrations": "1.*"
"illuminate/support": "5.*",
"illuminate/database": "5.*",
"doctrine/orm": "2.5.*@dev",
"doctrine/migrations": "1.0.*@dev"
},
"require-dev": {
"mockery/mockery": "dev-master",
2 changes: 1 addition & 1 deletion config/doctrine.php
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
'simple_annotations' => false,

'metadata' => [
base_path('app/models')
base_path('app/')
],

'proxy' => [
16 changes: 1 addition & 15 deletions src/Console/DqlCommand.php
Original file line number Diff line number Diff line change
@@ -21,21 +21,7 @@ class DqlCommand extends Command
*/
protected $description = 'Run a DQL query.';

/**
* The Entity Manager
*
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $entityManager;

public function __construct(EntityManagerInterface $entityManager)
{
parent::__construct();

$this->entityManager = $entityManager;
}

public function fire()
public function fire(EntityManagerInterface $entities)
{

}
22 changes: 4 additions & 18 deletions src/Console/GenerateProxiesCommand.php
Original file line number Diff line number Diff line change
@@ -19,29 +19,15 @@ class GenerateProxiesCommand extends Command
*/
protected $description = 'Generate proxies for entities.';

/**
* The Entity Manager
*
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $entityManager;

public function __construct(EntityManagerInterface $entityManager)
{
parent::__construct();

$this->entityManager = $entityManager;
}

public function fire()
public function fire(EntityManagerInterface $entityManager)
{
$this->info('Starting proxy generation....');
$metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
$metadata = $entityManager->getMetadataFactory()->getAllMetadata();
if (empty($metadata)) {
$this->error('No metadata found to generate any entities.');
exit;
}
$directory = $this->laravel['config']['doctrine::doctrine.proxy.directory'];
$directory = $this->laravel['config']['doctrine.proxy.directory'];
if ( ! $directory) {
$this->error('The proxy directory has not been set.');
exit;
@@ -50,7 +36,7 @@ public function fire()
foreach ($metadata as $item) {
$this->line($item->name);
}
$this->entityManager->getProxyFactory()->generateProxyClasses($metadata, $directory);
$entityManager->getProxyFactory()->generateProxyClasses($metadata, $directory);
$this->info('Proxies have been created.');
}
}
28 changes: 3 additions & 25 deletions src/Console/SchemaCreateCommand.php
Original file line number Diff line number Diff line change
@@ -21,42 +21,20 @@ class SchemaCreateCommand extends Command
*/
protected $description = 'Create database schema from models';

/**
* The schema tool.
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $tool;

/**
* The class metadata factory
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $metadata;

public function __construct(SchemaTool $tool, ClassMetadataFactory $metadata)
{
parent::__construct();

$this->tool = $tool;
$this->metadata = $metadata;
}

/**
* Execute the console command.
*
* @return void
*/
public function fire()
public function fire(SchemaTool $tool, ClassMetadataFactory $metadata)
{
if ($this->option('sql')) {
$this->info('Outputting create query:'.PHP_EOL);
$sql = $this->tool->getCreateSchemaSql($this->metadata->getAllMetadata());
$sql = $tool->getCreateSchemaSql($metadata->getAllMetadata());
$this->info(implode(';'.PHP_EOL, $sql));
} else {
$this->info('Creating database schema...');
$this->tool->createSchema($this->metadata->getAllMetadata());
$tool->createSchema($metadata->getAllMetadata());
$this->info('Schema has been created!');
}
}
28 changes: 3 additions & 25 deletions src/Console/SchemaDropCommand.php
Original file line number Diff line number Diff line change
@@ -21,36 +21,14 @@ class SchemaDropCommand extends Command
*/
protected $description = 'Drop database schema';

/**
* The schema tool.
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $tool;

/**
* The class metadata factory
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $metadata;

public function __construct(SchemaTool $tool, ClassMetadataFactory $metadata)
{
parent::__construct();

$this->tool = $tool;
$this->metadata = $metadata;
}

/**
* Execute the console command.
*
* @return void
*/
public function fire()
public function fire(SchemaTool $tool, ClassMetadataFactory $metadata)
{
$sql = $this->tool->getDropSchemaSQL($this->metadata->getAllMetadata());
$sql = $tool->getDropSchemaSQL($metadata->getAllMetadata());
if (empty($sql)) {
$this->info('Current models do not exist in schema.');
return;
@@ -60,7 +38,7 @@ public function fire()
$this->info(implode(';' . PHP_EOL, $sql));
} else {
$this->info('Dropping database schema....');
$this->tool->dropSchema($this->metadata->getAllMetadata());
$tool->dropSchema($metadata->getAllMetadata());
$this->info('Schema has been dropped!');
}
}
28 changes: 3 additions & 25 deletions src/Console/SchemaUpdateCommand.php
Original file line number Diff line number Diff line change
@@ -21,38 +21,16 @@ class SchemaUpdateCommand extends Command
*/
protected $description = 'Update database schema to match models';

/**
* The schema tool.
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $tool;

/**
* The class metadata factory
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
private $metadata;

public function __construct(SchemaTool $tool, ClassMetadataFactory $metadata)
{
parent::__construct();

$this->tool = $tool;
$this->metadata = $metadata;
}

/**
* Execute the console command.
*
* @return void
*/
public function fire()
public function fire(SchemaTool $tool, ClassMetadataFactory $metadata)
{
$this->info('Checking if database needs updating....');
$clean = $this->option('clean');
$sql = $this->tool->getUpdateSchemaSql($this->metadata->getAllMetadata(), $clean);
$sql = $tool->getUpdateSchemaSql($metadata->getAllMetadata(), $clean);
if (empty($sql)) {
$this->info('No updates found.');
return;
@@ -62,7 +40,7 @@ public function fire()
$this->info(implode(';' . PHP_EOL, $sql));
} else {
$this->info('Updating database schema....');
$this->tool->updateSchema($this->metadata->getAllMetadata());
$tool->updateSchema($metadata->getAllMetadata());
$this->info('Schema has been updated!');
}
}
28 changes: 14 additions & 14 deletions src/DoctrineUserProvider.php
Original file line number Diff line number Diff line change
@@ -2,14 +2,14 @@

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\UserProviderInterface;
use Illuminate\Hashing\HasherInterface;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher;

class DoctrineUserProvider implements UserProviderInterface
class DoctrineUserProvider implements UserProvider
{
/**
* @var HasherInterface
* @var Hasher
*/
private $hasher;
/**
@@ -22,11 +22,11 @@ class DoctrineUserProvider implements UserProviderInterface
private $entity;

/**
* @param HasherInterface $hasher
* @param Hasher $hasher
* @param EntityManager $entityManager
* @param $entity
*/
public function __construct(HasherInterface $hasher, EntityManager $entityManager, $entity)
public function __construct(Hasher $hasher, EntityManager $entityManager, $entity)
{
$this->hasher = $hasher;
$this->entityManager = $entityManager;
@@ -36,7 +36,7 @@ public function __construct(HasherInterface $hasher, EntityManager $entityManage
* Retrieve a user by their unique identifier.
* @param mixed $identifier
* @return UserInterface|null
* @return Authenticatable|null
*/
public function retrieveById($identifier)
{
@@ -48,7 +48,7 @@ public function retrieveById($identifier)
* @param mixed $identifier
* @param string $token
* @return UserInterface|null
* @return Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
@@ -62,11 +62,11 @@ public function retrieveByToken($identifier, $token)
/**
* Update the "remember me" token for the given user in storage.
* @param UserInterface $user
* @param Authenticatable $user
* @param string $token
* @return void
*/
public function updateRememberToken(UserInterface $user, $token)
public function updateRememberToken(Authenticatable $user, $token)
{
$user->setRememberToken($token);
$this->entityManager->persist($user);
@@ -77,7 +77,7 @@ public function updateRememberToken(UserInterface $user, $token)
* Retrieve a user by the given credentials.
* @param array $credentials
* @return UserInterface|null
* @return Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
@@ -92,11 +92,11 @@ public function retrieveByCredentials(array $credentials)
/**
* Validate a user against the given credentials.
* @param UserInterface $user
* @param Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(UserInterface $user, array $credentials)
public function validateCredentials(Authenticatable $user, array $credentials)
{
return $this->hasher->check($credentials['password'], $user->getAuthPassword());
}
71 changes: 62 additions & 9 deletions src/LaravelDoctrineServiceProvider.php
Original file line number Diff line number Diff line change
@@ -5,9 +5,12 @@
use Doctrine\ORM\Events;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Tools\Setup;
use Doctrine\Common\EventManager;
use Illuminate\Auth\AuthManager;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Database\Migrations\MigrationRepositoryInterface;
use Illuminate\Support\ServiceProvider;
use Mitch\LaravelDoctrine\Cache;
use Mitch\LaravelDoctrine\Configuration\DriverMapper;
@@ -18,6 +21,8 @@
use Mitch\LaravelDoctrine\EventListeners\SoftDeletableListener;
use Mitch\LaravelDoctrine\EventListeners\TablePrefix;
use Mitch\LaravelDoctrine\Filters\TrashedFilter;
use Mitch\LaravelDoctrine\Migrations\DoctrineMigrationRepository;
use Mitch\LaravelDoctrine\Passwords\DoctrineTokenRepository;
use Mitch\LaravelDoctrine\Validation\DoctrinePresenceVerifier;

class LaravelDoctrineServiceProvider extends ServiceProvider
@@ -30,8 +35,12 @@ class LaravelDoctrineServiceProvider extends ServiceProvider

public function boot()
{
$this->package('mitchellvanw/laravel-doctrine', 'doctrine', __DIR__ . '/..');
$this->publishes([
__DIR__.'/../config/doctrine.php' => config_path('doctrine.php'),
]);

$this->extendAuthManager();
$this->extendMigrator();
}

/**
@@ -52,6 +61,10 @@ public function register()
'Mitch\LaravelDoctrine\Console\SchemaUpdateCommand',
'Mitch\LaravelDoctrine\Console\SchemaDropCommand'
]);

$this->mergeConfigFrom(
__DIR__.'/../config/doctrine.php', 'doctrine'
);
}

/**
@@ -75,16 +88,18 @@ private function registerConfigurationMapper()
*/
public function registerValidationVerifier()
{
$this->app->bindShared('validation.presence', function()
$this->app->singleton('validation.presence', function()
{
return new DoctrinePresenceVerifier(EntityManagerInterface::class);
return new DoctrinePresenceVerifier(function() {
return $this->app->make(EntityManagerInterface::class);
});
});
}

public function registerCacheManager()
{
$this->app->bind(CacheManager::class, function ($app) {
$manager = new CacheManager($app['config']['doctrine::doctrine.cache']);
$manager = new CacheManager($app['config']['doctrine.cache']);
$manager->add(new Cache\ApcProvider);
$manager->add(new Cache\MemcacheProvider);
$manager->add(new Cache\RedisProvider);
@@ -97,7 +112,12 @@ public function registerCacheManager()
private function registerEntityManager()
{
$this->app->singleton(EntityManager::class, function ($app) {
$config = $app['config']['doctrine::doctrine'];
$config = $app['config']['doctrine'];

$config['metadata'] = array_merge($config['metadata'], [
__DIR__ . '/Migrations'
]);

$metadata = Setup::createAnnotationMetadataConfiguration(
$config['metadata'],
$app['config']['app.debug'],
@@ -145,14 +165,47 @@ private function extendAuthManager()
{
$this->app[AuthManager::class]->extend('doctrine', function ($app) {
return new DoctrineUserProvider(
$app['Illuminate\Hashing\HasherInterface'],
$app[Hasher::class],
$app[EntityManager::class],
$app['config']['auth.model']
);
});
}


private function extendMigrator()
{
$this->app->singleton('migration.repository', function($app) {
return new DoctrineMigrationRepository(
function() use($app) {
return $app->make(EntityManagerInterface::class);
},
function() use($app) {
return $app->make(SchemaTool::class);
},
function() use($app) {
return $app->make(ClassMetadataFactory::class);
}
);
});
$this->app->bind(MigrationRepositoryInterface::class, 'migration.repository');
}

/**
* Get the services provided by the provider.
* @return array
*/
public function provides()
{
return [
CacheManager::class,
EntityManagerInterface::class,
EntityManager::class,
ClassMetadataFactory::class,
DriverMapper::class,
AuthManager::class,
];
}

/**
* Map Laravel's to Doctrine's database configuration requirements.
* @param $config
@@ -163,6 +216,6 @@ private function mapLaravelToDoctrineConfig($config)
{
$default = $config['database.default'];
$connection = $config["database.connections.{$default}"];
return App::make(DriverMapper::class)->map($connection);
return $this->app->make(DriverMapper::class)->map($connection);
}
}
196 changes: 196 additions & 0 deletions src/Migrations/DoctrineMigrationRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php namespace Mitch\LaravelDoctrine\Migrations;

use Doctrine\ORM\QueryBuilder;
use Exception;
use Illuminate\Database\Migrations\MigrationRepositoryInterface;

class DoctrineMigrationRepository implements MigrationRepositoryInterface {

/**
* The entity manager
*
* @var \Doctrine\ORM\EntityManagerInterface
*/
protected $entities;

/**
* The schema tool
*
* @var \Doctrine\ORM\Tools\SchemaTool
*/
protected $schema;

/**
* The metadata factory
*
* @var \Doctrine\ORM\Mapping\ClassMetadataFactory
*/
protected $metadata;

/**
* Create a new database migration repository instance.
*
* @param callable $entitiesCallback
* @param callable $schemaCallback
* @param callable $metadataCallback
*/
public function __construct(callable $entitiesCallback, callable $schemaCallback, callable $metadataCallback)
{
$this->entitiesCallback = $entitiesCallback;
$this->schemaCallback = $schemaCallback;
$this->metadataCallback = $metadataCallback;
}

/**
* Get the ran migrations.
*
* @return array
*/
public function getRan()
{
$migrations = $this->query()
->getQuery()->getResult();

$return = [];

foreach($migrations as $migration) {
$return[] = $migration['migration'];
}

return $return;
}

/**
* Get the last migration batch.
*
* @return array
*/
public function getLast()
{
return $this->query()
->where('o.batch = :lastBatch')
->setParameter('lastBatch', $this->getLastBatchNumber())
->orderBy('o.migration', 'desc')->getQuery()->getResult();
}
/**
* Log that a migration was run.
*
* @param string $file
* @param int $batch
* @return void
*/
public function log($file, $batch)
{
$migration = new Migration($file, $batch);
$this->getEntities()->persist($migration);
$this->getEntities()->flush();
}
/**
* Remove a migration from the log.
*
* @param object $migration
* @return void
*/
public function delete($migration)
{
$this->getEntities()->createQueryBuilder()
->delete('Mitch\LaravelDoctrine\Migrations\Migration', 'o')
->andWhere('o.migration = :migration')
->setParameter('migration', $migration->migration)
->getQuery()
->execute();
}
/**
* Get the next migration batch number.
*
* @return int
*/
public function getNextBatchNumber()
{
return $this->getLastBatchNumber() + 1;
}
/**
* Get the last migration batch number.
*
* @return int
*/
public function getLastBatchNumber()
{
$result = $this->getEntities()->createQueryBuilder()
->select('o, MAX(o.batch) as max_batch')
->from('Mitch\LaravelDoctrine\Migrations\Migration', 'o')
->getQuery()->getResult()[0]['max_batch'];

return $result ?: 0;
}
/**
* Create the migration repository data store.
*
* @return void
*/
public function createRepository()
{
$this->getSchemaTool()->updateSchema($this->getMetadata()->getAllMetadata());
}
/**
* Determine if the migration repository exists.
*
* @return bool
*/
public function repositoryExists()
{
$schema = $this->getEntities()->getConnection()->getSchemaManager();
$tables = array_filter($schema->listTables(), function($value) {
return $value->getName() === 'migrations';
});

return !empty($tables);
}
/**
* Get a query builder for the migration table.
*
* @return QueryBuilder
*/
protected function query()
{
return $this->getEntities()->createQueryBuilder()
->select('o')
->from('Mitch\LaravelDoctrine\Migrations\Migration', 'o');
}

/**
* Set the information source to gather data.
*
* @param string $name
* @throws \Exception
* @return void
*/
public function setSource($name) {
// not implemented
}

protected function getEntities() {
if($this->entities == null) {
$callable = $this->entitiesCallback;
$this->entities = $callable();
}
return $this->entities;
}

protected function getSchemaTool() {
if($this->schema == null) {
$callable = $this->schemaCallback;
$this->schema = $callable();
}
return $this->schema;
}

protected function getMetadata() {
if($this->metadata == null) {
$callable = $this->metadataCallback;
$this->metadata = $callable();
}
return $this->metadata;
}

}
37 changes: 37 additions & 0 deletions src/Migrations/Migration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Mitch\LaravelDoctrine\Migrations;

use Doctrine\ORM\Mapping AS ORM;

/**
* @ORM\Entity
* @ORM\Table(name="migrations")
* @ORM\HasLifecycleCallbacks
*/
class Migration {

/**
* @ORM\Id
* @ORM\Column(type="string")
*/
public $migration;

/**
* @ORM\Column(type="integer")
*/
public $batch;

/**
* Constructs the Migration.
*
* @param $migration
* @param $batch
*/
public function __construct($migration, $batch)
{
$this->migration = $migration;
$this->batch = $batch;
}

}
159 changes: 159 additions & 0 deletions src/Passwords/DoctrineTokenRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

namespace Mitch\LaravelDoctrine\Passwords;

use Carbon\Carbon;
use Doctrine\ORM\EntityManagerInterface;
use Illuminate\Auth\Passwords\TokenRepositoryInterface;
use Illuminate\Contracts\Auth\CanResetPassword;

class DoctrineTokenRepository implements TokenRepositoryInterface {

/**
* Constructs the repository.
*
* @param EntityManagerInterface $entities
* @param string $hashKey
* @param int $expires
*/
public function __construct(EntityManagerInterface $entities, $hashKey, $expires = 60)
{
$this->entities = $entities;
$this->expires = $expires * 60;
$this->hashKey = $hashKey;
}

/**
* Create a new reminder record and token.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return string
*/
public function create(CanResetPassword $user)
{
$email = $user->getEmailForPasswordReset();

$this->deleteExisting($user);

// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken($user);

$reminder = new PasswordReminder($email, $token);
$this->entities->persist($reminder);
$this->entities->flush();

return $token;
}

/**
* Create a new token for the user.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return string
*/
protected function createNewToken(CanResetPassword $user)
{
$email = $user->getEmailForPasswordReset();

$value = str_shuffle(sha1($email.spl_object_hash($this).microtime(true)));

return hash_hmac('sha1', $value, $this->hashKey);
}

/**
* Delete all existing reset tokens from the database.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @return int
*/
protected function deleteExisting(CanResetPassword $user)
{
return $this->makeDelete()
->where('o.email = :email')
->setParameter('email', $user->getEmailForPasswordReset())
->getQuery()
->execute();
}

/**
* Determine if a reminder record exists and is valid.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $token
* @return bool
*/
public function exists(CanResetPassword $user, $token)
{
$email = $user->getEmailForPasswordReset();

$reminder = $this->makeSelect()
->where('o.email = :email')
->andWhere('o.token = :token')
->setParameter('email', $email)
->setParameter('token', $token)
->getQuery()
->getOneOrNullResult();

return $reminder != null && !$this->reminderExpired($reminder);
}

/**
* Determine if the reminder has expired.
*
* @param PasswordReminder $reminder
* @return bool
*/
protected function reminderExpired(PasswordReminder $reminder)
{
$createdPlusHour = $reminder->getCreatedAt()->getTimestamp() + $this->expires;

return $createdPlusHour < time();
}

/**
* Delete a reminder record by token.
*
* @param string $token
* @return void
*/
public function delete($token)
{
$this->makeDelete()
->where('o.token = :token')
->setParameter('token', $token)
->getQuery()
->execute();
}

/**
* Delete expired reminders.
*
* @return void
*/
public function deleteExpired()
{
$expired = Carbon::now()->subSeconds($this->expires);

$this->makeDelete()
->where('o.createdAt < :expired')
->setParameter('expired', $expired)
->getQuery()
->execute();
}

protected function makeSelect()
{
return $this->entities->createQueryBuilder()
->select('o')
->from(PasswordReminder::class, 'o');
}

protected function makeDelete()
{
return $this->entities->createQueryBuilder()
->delete(PasswordReminder::class, 'o');
}

}
57 changes: 57 additions & 0 deletions src/Passwords/PasswordReminder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Mitch\LaravelDoctrine\Passwords;

use DateTime;
use Doctrine\ORM\Mapping AS ORM;

/**
* @ORM\Entity
* @ORM\Table(name="password_resets")
*/
class PasswordReminder {

/**
* @ORM\Id
* @ORM\Column(type="string")
*/

protected $email;

/**
* @ORM\Column(type="string")
*/

protected $token;

/**
* @ORM\Column(name="created_at", type="datetime", nullable=false)
* @var DateTime
*/

private $createdAt;

/**
* Constructs the PasswordReminder.
*
* @param string $email
* @param string $token
*/

public function __construct($email, $token) {
$this->email = $email;
$this->token = $token;
$this->createdAt = new DateTime();
}

/**
* Returns when the reminder was created.
*
* @return DateTime
*/

public function getCreatedAt() {
return $this->createdAt;
}

}
99 changes: 99 additions & 0 deletions src/Passwords/PasswordResetServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php


namespace Mitch\LaravelDoctrine\Passwords;

use Doctrine\ORM\EntityManagerInterface;
use Illuminate\Auth\Passwords\PasswordBroker;
use Illuminate\Support\ServiceProvider;

class PasswordResetServiceProvider extends ServiceProvider {

/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;

/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->loadEntitiesFrom(__DIR__);

$this->registerPasswordBroker();

$this->registerTokenRepository();
}

/**
* Register a directory of Doctrine entities.
*
* @param string $directory
* @return void
*/
public function loadEntitiesFrom($directory)
{
$metadata = $this->app['config']['doctrine.metadata'];
$metadata[] = $directory;
$this->app['config']->set('doctrine.metadata', $metadata);
}

/**
* Register the password broker instance.
*
* @return void
*/
protected function registerPasswordBroker()
{

$this->app->singleton('auth.password', function ($app) {
// The password token repository is responsible for storing the email addresses
// and password reset tokens. It will be used to verify the tokens are valid
// for the given e-mail addresses. We will resolve an implementation here.
$tokens = $app['auth.password.tokens'];

$users = $app['auth']->driver()->getProvider();

$view = $app['config']['auth.password.email'];

// The password broker uses a token repository to validate tokens and send user
// password e-mails, as well as validating that password reset process as an
// aggregate service of sorts providing a convenient interface for resets.
return new PasswordBroker(
$tokens, $users, $app['mailer'], $view
);
});
}

/**
* Register the token repository implementation.
*
* @return void
*/
protected function registerTokenRepository()
{
$this->app->singleton('auth.password.tokens', function ($app) {
$key = $app['config']['app.key'];

$expire = $app['config']->get('auth.reminder.expire', 60);

return new DoctrineTokenRepository($this->app->make(EntityManagerInterface::class), $key, $expire);
});
}

/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['auth.password', 'auth.password.tokens'];
}

}
18 changes: 14 additions & 4 deletions src/Validation/DoctrinePresenceVerifier.php
Original file line number Diff line number Diff line change
@@ -8,9 +8,13 @@

class DoctrinePresenceVerifier implements PresenceVerifierInterface
{

/**
* @var callable
*/
protected $entityManager;

public function __construct(EntityManagerInterface $entityManager)
public function __construct(callable $entityManager)
{
$this->entityManager = $entityManager;
}
@@ -25,7 +29,7 @@ public function __construct(EntityManagerInterface $entityManager)
* @param array $extra
* @return int
*/
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = array())
public function getCount($collection, $column, $value, $excludeId = null, $idColumn = null, array $extra = [])
{
$queryParts = ['SELECT COUNT(*) FROM', $collection, 'WHERE', "$column = ?"];

@@ -60,7 +64,7 @@ public function getCount($collection, $column, $value, $excludeId = null, $idCol
* @param array $extra
* @return int
*/
public function getMultiCount($collection, $column, array $values, array $extra = array())
public function getMultiCount($collection, $column, array $values, array $extra = [])
{
$queryParts = ['SELECT COUNT(*) FROM', $collection, 'WHERE', "$column IN (?)"];

@@ -88,6 +92,12 @@ private function createQueryFrom(array $queryParts = [])
{
$rsm = new ResultSetMapping();

return $this->entityManager->createNativeQuery(implode(' ', $queryParts), $rsm);
return $this->getEntityManager()->createNativeQuery(implode(' ', $queryParts), $rsm);
}

private function getEntityManager() {
$callable = $this->entityManager;
return $callable();
}

}
159 changes: 159 additions & 0 deletions tests/Migrations/MigrationsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php namespace Tests\Migrations;

use Mitch\LaravelDoctrine\Migrations\DoctrineMigrationRepository;
use Mitch\LaravelDoctrine\Migrations\Migration;
use Mockery as m;

class MigrationsTest extends \PHPUnit_Framework_TestCase
{

public function getMockRepository($em = null, $schema = null, $metadata = null) {
$entities = function() use($em) {
return $em != null ? $em : m::mock('Doctrine\ORM\EntityManager');
};

$schema = function() use($schema) {
return $schema != null ? $schema : m::mock('Doctrine\ORM\Tools\SchemaTool');
};

$metadata = function() use($metadata) {
return $metadata != null ? $metadata : m::mock('Doctrine\ORM\Mapping\ClassMetadataFactory');
};

return new DoctrineMigrationRepository($entities, $schema, $metadata);
}

public function getMockQuery() {
return m::mock('\Doctrine\ORM\AbstractQuery');
/*->disableOriginalConstructor()
->getMockForAbstractClass();*/
}

public function testGetRan()
{
$q = $this->getMockQuery();
$q->shouldReceive('getResult')->andReturn([
[
'migration' => '2014_04_19_060008_create_pages_table',
'batch' => '1'
],
[
'migration' => '2014_04_19_062803_create_login_log_table',
'batch' => '2'
]
]);

$qb = m::mock('Doctrine\ORM\QueryBuilder');
$qb->shouldReceive('select')->andReturnSelf();
$qb->shouldReceive('from')->with('Mitch\LaravelDoctrine\Migrations\Migration', m::any())->andReturnSelf();
$qb->shouldReceive('getQuery')->andReturn($q);

$em = m::mock('Doctrine\ORM\EntityManager');
$em->shouldReceive('createQueryBuilder')->andReturn($qb);

$repository = $this->getMockRepository($em);

$this->assertEquals($repository->getRan(), [
'2014_04_19_060008_create_pages_table',
'2014_04_19_062803_create_login_log_table'
]);
}

public function testLog()
{
$em = m::mock('Doctrine\ORM\EntityManager');
$em->shouldReceive('persist');
$em->shouldReceive('flush');

$repository = $this->getMockRepository($em);
$repository->log('2013_04_19_064008_create_pages_table', 169);
}

public function testDelete()
{
$q = $this->getMockQuery();
$q->shouldReceive('execute');

$qb = m::mock('Doctrine\ORM\QueryBuilder');
$qb->shouldReceive('delete')->with('Mitch\LaravelDoctrine\Migrations\Migration', m::any())->andReturnSelf();
$qb->shouldReceive('andWhere')->andReturnSelf();
$qb->shouldReceive('setParameter')->andReturnSelf();
$qb->shouldReceive('getQuery')->andReturn($q);

$em = m::mock('Doctrine\ORM\EntityManager');
$em->shouldReceive('createQueryBuilder')->andReturn($qb);

$repository = $this->getMockRepository($em);

$repository->delete(new Migration('2014_04_19_060008_create_pages_table', 169));
}

public function testGetBatchNumber()
{
$q = $this->getMockQuery();
$q->shouldReceive('getResult')->andReturn([
[
'max_batch' => 4
]
]);

$qb = m::mock('Doctrine\ORM\QueryBuilder');
$qb->shouldReceive('select')->andReturnSelf();
$qb->shouldReceive('from')->with('Mitch\LaravelDoctrine\Migrations\Migration', m::any())->andReturnSelf();
$qb->shouldReceive('getQuery')->andReturn($q);

$em = m::mock('Doctrine\ORM\EntityManager');
$em->shouldReceive('createQueryBuilder')->andReturn($qb);

$repository = $this->getMockRepository($em);

$this->assertEquals($repository->getLastBatchNumber(), 4);
$this->assertEquals($repository->getNextBatchNumber(), 5);
}

public function testCreateRepository()
{
$schema = m::mock('Doctrine\ORM\Tools\SchemaTool');
$schema->shouldReceive('updateSchema')->with(['schema' => ['goes', 'here']]);

$metadata = m::mock('Doctrine\ORM\Mapping\ClassMetadataFactory');
$metadata->shouldReceive('getAllMetadata')->andReturn(['schema' => ['goes', 'here']]);

$repository = $this->getMockRepository(m::mock('Doctrine\ORM\EntityManager'), $schema, $metadata);

$repository->createRepository();
}

public function testRepositoryExists()
{
$schema = m::mock();
$schema->shouldReceive('listTables')->andReturn([
new Table('pages'), new Table('partials'), new Table('users'), new Table('preferences'), new Table('migrations')
]);

$connection = m::mock('Doctrine\DBAL\Connection');
$connection->shouldReceive('getSchemaManager')
->andReturn($schema);

$em = m::mock('Doctrine\ORM\EntityManager');
$em->shouldReceive('getConnection')->andReturn($connection);

$repository = $this->getMockRepository($em);

$this->assertTrue($repository->repositoryExists());
}

}

class Table {

protected $name;

public function __construct($name) {
$this->name = $name;
}

public function getName() {
return $this->name;
}
}
11 changes: 11 additions & 0 deletions tests/Reminders/RemindersTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php namespace Tests\Reminders;

use Mockery as m;

class RemindersTest extends \PHPUnit_Framework_TestCase
{
public function testThings()
{

}
}