I have read how to validate forms in server side with sf2. The solution is by using the Constraints in the Entity as annotations, validation.yml or inside the EntityType (Form).
Everything is fine, however, all of these validations work just with the form. But when you instance a new object and try to persist, validation doesn't work.
I will give you an example.
Imagine I have a user entity:
/**
* #ORM\Entity
* #ORM\Table(name="sf_user")
*/
class User{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column( name="username", type="string", length=50, unique=true )
*/
protected $username;
/**
* #var string
* #ORM\Column( name="email", type="string", length=100, unique=true )
*/
protected $email;
public static function loadValidatorMetadata(\Symfony\Component\Validator\Mapping\ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('username', new \Symfony\Component\Validator\Constraints\NotBlank());
$metadata->addPropertyConstraint('email', new \Symfony\Component\Validator\Constraints\NotNull());
}
}
Then, in some controller I try to save my form with:
$this->form = $this->create(new UserType());
$this->form->setData(new User());
$this->form->bind($this->request);
if( $this->form->isValid())
{
//Persist with entity manager
}
Everything works perfectly because I have an association between my Entity and my form. But what happen if i need to instance an object without a form?. I should do something like this:
$user = new User();
$user->setUsername("username");
//Persist with entity manager
If I do that, entity is not validated and DB throws an error because the field "email" is required.
Should I always associate my entity with the form to validate? If that is the case, I don't agree at all because if I am working with web services, I don't wanna create a form just to validate on the server side.
So, how could I do this validation?. Thanks for your help.
You can use the validation service
$validator = $this->get('validator');
$validator->validate($user);
see the docs about this.
By the way there is a cleaner way to specify validation in you entity.
use Symfony\Component\Validator\Constraints as Assert;
class User{
/**
* #Assert\NotNull
*/
protected $username;
/**
* #Assert\NotBlank
* #Assert\Email
*/
protected $email;
Related
I am trying to build and API, and I am using passport for authentication, the problem is I want all table names to start with capital letter for example "Oauth" instead of "oauth", how to achieve this without directly modifying the vendor files?
You can't directly modify table names but you can modify the model names that passport uses.
If you look at Passport Class there are static class variables:
/**
* The auth code model class name.
*
* #var string
*/
public static $authCodeModel = 'Laravel\Passport\AuthCode';
/**
* The client model class name.
*
* #var string
*/
public static $clientModel = 'Laravel\Passport\Client';
/**
* The personal access client model class name.
*
* #var string
*/
public static $personalAccessClientModel = 'Laravel\Passport\PersonalAccessClient';
/**
* The token model class name.
*
* #var string
*/
public static $tokenModel = 'Laravel\Passport\Token';
/**
* The refresh token model class name.
*
* #var string
*/
public static $refreshTokenModel = 'Laravel\Passport\RefreshToken';
and etc.
These are models that passport uses to communicate with database. You can make your own model and extend Passport models.
For example:
class MyOwnAuthCodeModel extends Laravel\Passport\AuthCode {
public $table = 'Auth_codes';
}
Then in Passport ServiceProvider File you have to change models:
Passport::$clientModel = '\App\Models\MyOwnAuthCodeModel';
...
Then you have to make migration files to rename all passport tables:
Schema::rename('oauth_codes', 'Oauth_codes');
...
That steps is too much if you don't want to change table names in vendor migrations and models.
And it's ready to work correctly.
Remember you have to do this for all passport tables and models.
I need to get an original Request (specifically Request::server()) in my listeners for these Laravel internal events:
Illuminate\Auth\Events\Login
Illuminate\Auth\Events\Failed
Understandably, I cannot use values Request returns in my listener, since it's constructed separately server-side on queue.
Any help is highly appreciated!
In the constructor of the listener you can save the request to a member of the class, then you will be able to use it inside the handle function. For example:
class LogSuccessfulLogin implements ShouldQueue
{
protected $request;
/**
* Create the event listener.
*
* #return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Handle the event.
*
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
// here you can use $this->request->ip(); for example.
}
}
Documentation on laravel.com is not sufficient. Can any one guide me through how to How To Create contracts in Laravel from scratch.
I need implementation of Contracts in Laravel. Right now, I'm using Laravel 5.4
Contract is just a fancy name for php interfaces. We have being using them all along and its not a new thing.
Contracts/Interfaces help us to maintain a loosely coupled code base. See the example from doc below.
<?php
namespace App\Orders;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* #param \SomePackage\Cache\Memcached $cache
* #return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* Retrieve an Order by ID.
*
* #param int $id
* #return Order
*/
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
Here when ever the Repository instantiate we should give a \SomePackage\Cache\Memcached instance in order for code to work. Hence our code is tightly coupled with \SomePackage\Cache\Memcached. Now look at below code.
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* The cache instance.
*/
protected $cache;
/**
* Create a new repository instance.
*
* #param Cache $cache
* #return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
Same thing but now we just need to provide some cache interface. And behind the scene you could have done something like this.
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class RedisCache implements Cache {
//
}
When above Repository instantiate, php will look at the Illuminate\Contracts\Cache\Repository and It has been implemented by RedisCache class.
I'm afraid Gayan's answer needs further elaboration to hit Rajan's question.
Yes Gayan is correct that creating a Contract class basically means creating a php interface.
Continuing the Cache example above, if we look into its source code (you can find it at this Github repo file), we can see something like this
<?php
namespace Illuminate\Contracts\Cache;
use Closure;
interface Repository
{
/**
* Determine if an item exists in the cache.
*
* #param string $key
* #return bool
*/
public function has($key);
/**
* Retrieve an item from the cache by key.
*
* #param string $key
* #param mixed $default
* #return mixed
*/
public function get($key, $default = null);
// the rest...
}
If we are using this interface in our laravel app, it is said to be a "Contract". It is declaring what methods/properties a class should have if it implements this interface. For example in our app...
<?php
namespace App\Whatever;
use Illuminate\Contracts\Cache\Repository;
class Foo implements Repository {
//
}
Then class Foo will need to have methods has and get in order to fulfil what has been stated in the Repository contract.
I am using Laravel and I want to use JSon Web Token (JWT). I download the tymon vendor. When I tried to generate the token it raise me an error said that my model is not an instance of Eloquent model. So I check the vendor code and I saw this in EloquentUserAdapter:
<?php
namespace Tymon\JWTAuth\Providers\User;
use Illuminate\Database\Eloquent\Model;
class EloquentUserAdapter implements UserInterface
{
/**
* #var \Illuminate\Database\Eloquent\Model
*/
protected $user;
/**
* Create a new User instance
*
* #param \Illuminate\Database\Eloquent\Model $user
*/
public function __construct(Model $user)
{
$this->user = $user;
}
/**
* Get the user by the given key, value
*
* #param mixed $key
* #param mixed $value
* #return Illuminate\Database\Eloquent\Model
*/
public function getBy($key, $value)
{
return $this->user->where($key, $value)->first();
}
}
My problem here is that this adapter only use Eloquent model injection. I am using Doctrine models. So my questions are:
Is possible to change this adapter to return new model (my doctrine
model). I asking because I am new in PHP and Laravel and I saw that
EloquentUserAdapter is used in other places in the vendor.
If I create a new Adapter I think that I have to return a Eloquent
model, so how do I can redefine only the model and reuse the other
classes and methods of the vendor?
Any clue?
Yes, you can:
use App\Entities\User;
use Doctrine\ORM\EntityManagerInterface;
use Tymon\JWTAuth\Providers\User\UserInterface;
class DoctrineUserAdapter implements UserInterface
{
protected $em;
public function __construct(User $user, EntityManagerInterface $em)
{
$this->em = $em;
}
public function getBy($key, $value)
{
return $this->em->find('App\Entities\User', $value);
}
}
You can inject EntityManagerInterface object as a second parameter, first parameter is a User model type from 'providers.user' in jwt.php configuration, why? Look at the code in JWTAuthServiceProvider.php:
/**
* Register the bindings for the User provider.
*/
protected function registerUserProvider()
{
$this->app['tymon.jwt.provider.user'] = $this->app->share(function ($app) {
return $app->make($this->config('providers.user'), [$app->make($this->config('user'))]);
});
}
My simple user model:
use DOctrine\ORM\Mapping as ORM;
/**
* Users *
* #ORM\Table(name="users")
* #ORM\Entity
*/
class User implements \Illuminate\Contracts\Auth\Authenticatable
{
use \LaravelDoctrine\ORM\Auth\Authenticatable;
/**
*
* #var integer *
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
public $id;
}
You can do this in version 1.0.0. More about problem: https://github.com/tymondesigns/jwt-auth/issues/343
Hi I am creating a custom cache service class which will abstract the caching layer out of my repository. However I am running into some trouble as I'm getting this error:
Argument 1 passed to Task::__construct() must implement interface MyApp\Cache\CacheInterface, none given, called in /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php on line 792 and defined
My class is as so follows:
<?php namespace MyApp\Cache;
use Illuminate\Cache\CacheManager;
class CacheService {
/**
* #var Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* #var integer
*/
protected $minutes;
/**
* Construct
*
* #param Illuminate\Cache\CacheManager $cache
* #param string $tag
* #param integer $minutes
*/
public function __construct(CacheManager $cache, $minutes = 60)
{
$this->cache = $cache;
$this->tag = $tag;
$this->minutes = $minutes;
}
/**
* Get
*
* #param string $key
* #return mixed
*/
public function get($key)
{
return $this->cache->tags($this->tag)->get($key);
}
/**
* Put
*
* #param string $key
* #param mixed $value
* #param integer $minutes
* #return mixed
*/
public function put($key, $value, $minutes = null)
{
if( is_null($minutes) )
{
$minutes = $this->minutes;
}
return $this->cache->tags($this->tag)->put($key, $value, $minutes);
}
/**
* Has
*
* #param string $key
* #return bool
*/
public function has($key)
{
return $this->cache->tags($this->tag)->has($key);
}
}
And in my model I have the following;
<?php
use Abstracts\Model as AbstractModel;
use Illuminate\Support\Collection;
use CMS\APIv2\Objects\Entity;
use MyApp/Cache\CacheInterface;
class SprintTask extends AbstractModel
{
/**
* #var CacheInterface
*/
protected $cache;
public function __construct(CacheInterface $cache)
{
$this->cache = $cache;
}
public static function scopegetAssignedSprint($id) {
$key = md5('id.'.$id.get_class());
if($this->cache->has($key))
{
return $this->cache->get($key);
}
$user = static::where('uid', $id)->lists('sprint_id');
$this->cache->put($key, $user);
return $user;
}
And I have a cache service provider which is as follows;
<?php
namespace MyApp\Cache;
use MyApp\Cache\CacheInterface;
use Illuminate\Support\ServiceProvider;
class CacheServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Register
*/
public function register()
{
$this->app->bind
('MyApp\Cache\CacheInterface',
'MyApp\Cache\CacheService');
}
}
Any ideas how I can setup this service provider correctly to be used in any mode/controller/repo etc??
have you tried to do it like this :
<?php
namespace MyApp\Cache;
use MyApp\Cache\CacheInterface;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\CacheManager;
class CacheServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = false;
/**
* Register
*/
public function register()
{
$this->app->bind('MyApp\Cache\CacheInterface',function(){
return new CacheService(CacheManager $cache);
});
}
}
I just updated it bases on Trip comment
When service container tries to instantiate TaskRepository, it sees that one of its constructor arguments is an object of class CacheService. Therefore it first tries to instantiate this object so that it can be later passed to TaskRepository constructor.
Your CacheService defines two required arguments. When Container tries to instantiate CacheService, it needs to provide values for both attributes and pass them to the constructor. Service container normally uses the type hint of constructor arguments to identify what service should be injected. In your case you require $tag variable to be passed, but as it has no type hint in constructor signature, therefore service container has no idea what should be passed to the constructor.
That's why you're getting the error - it simply says that service container is not able to resolve one of required dependencies of CacheService class.
There are multiple solutions to that problem.
First of all, you'll need to add a type hint to $tags argument.
If $tags is an object of some class that service container is able to instantiate, add a type hint to CacheService constructor signature.
If instantiating $tags object is something that you want to handle yourself, create the object in one of your service providers and bind it using container's bind() method.
If $tags is soomething that cannot be managed and injected by the container, you'll need to instantiate CacheService yourself and bind it using bind().
If $tags is something that cannot be managed by service container, e.g. array, you'll need to instantiate TaskRepository yourself, not via the service container.
You can read more about dependency injection in Laravel here: http://laravel.com/docs/5.0/container
The short version is that you can't use dependency injection on an Eloquent model. The IoC magic doesn't work on them, and since your AbstractModel extends the Eloquent Model class, the IoC magic doesn't work here. (Partial explanation by Taylor Otwell -- https://github.com/laravel/framework/issues/3862#issuecomment-37364543)
There are a couple of ways to get this basic idea to work:
(1) Use a Repository for the Model and inject your CacheService there. Since Eloquent is a repository-like ORM, this can be confusing and tedious.
(2) Register your CacheService through a new Facade and use it like Laravel's built-in Cache facade, directly in your model without injection -- MyCache::has($key). It can be set up as a Singleton if you'll never need to connect to two different caches. (L4: http://laravel.com/docs/4.2/facades, L5: http://laravel.com/docs/5.1/facades)
(3) Use a combination of #1 and #2, as outlined here: http://dfg.gd/blog/decoupling-your-code-in-laravel-using-repositiories-and-services
You're trying to inject an instance implementing CacheInterface into SprintTask, but in your service provider, you're providing an instance of CacheService which does not implement CacheInterface.
You need to implement that interface in CashService in order to be able to pass it to SpringTask:
class CacheService implements CacheInterface