I'm trying to understand PHP Laravel Framework. Whend dealing with databases, we use Schema::table to create a table in the database. Searching in my application I find the only definition is
<?php namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Database\Schema\Builder
*/
class Schema extends Facade {
/**
* Get a schema builder instance for a connection.
*
* #param string $name
* #return \Illuminate\Database\Schema\Builder
*/
public static function connection($name)
{
return static::$app['db']->connection($name)->getSchemaBuilder();
}
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return static::$app['db']->connection()->getSchemaBuilder();
}
}
but there is not a table static method neither in Schema class or in Facade class.
What piece I'm missing?
Schema here is only facade. Method table() and other methods from Schema can be found in file: \vendor\laravel\framework\src\Illuminate\Database\Schema\Builder.php .
Other methods can be found in \vendor\laravel\framework\src\Illuminate\Database\Schema\Blueprint.php
You should always look at Facade Class Reference to know what class you are really using.
Related
Prerequisites:
PHP 7.1.8
Symfony 3.3.9
Doctrine 2.6.x-dev
I wonder if it's possible to override an inversedBy attribute of a property association mapping that's taken from a trait.
An interface that I use as a concrete user entity placeholder:
ReusableBundle\ModelEntrantInterface.php
interface EntrantInterface
{
public function getEmail();
public function getFirstName();
public function getLastName();
}
The following architecture works just fine (need to create User entity that implements EntrantInterface and all other entities that are derived from these abstract classes in AppBundle):
ReusableBundle\Entity\Entry.php
/**
* #ORM\MappedSuperclass
*/
abstract class Entry
{
/**
* #var EntrantInterface
*
* #ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface", inversedBy="entries")
* #ORM\JoinColumn(name="user_id")
*/
protected $user;
// getters/setters...
}
ReusableBundle\Entity\Timestamp.php
/**
* #ORM\MappedSuperclass
*/
abstract class Timestamp
{
/**
* #var EntrantInterface
*
* #ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface", inversedBy="timestamps")
* #ORM\JoinColumn(name="user_id")
*/
protected $user;
// getters/setters...
}
And couple more entities with similar structure that utilize EntranInterface
And this is what I want to achieve - UserAwareTrait to be reusable across several entities:
ReusableBundle\Entity\Traits\UserAwareTrait.php
trait UserAwareTrait
{
/**
* #var EntrantInterface
*
* #ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface")
* #ORM\JoinColumn(name="user_id")
*/
protected $user;
// getter/setter...
}
In Doctrine 2.6 if I would use super class and wanted to override its property I'd do this:
/**
* #ORM\MappedSuperclass
* #ORM\AssociationOverrides({
* #ORM\AssociationOverride({name="property", inversedBy="entities"})
* })
*/
abstract class Entity extends SuperEntity
{
// code...
}
But if I want that Entity to use UserAwareTrait and override association mapping of a property...
/**
* #ORM\MappedSuperclass
* #ORM\AssociationOverrides({
* #ORM\AssociationOverride({name="user", inversedBy="entries"})
* })
*/
abstract class Entry
{
use UserAwareTrait;
// code...
}
... and run php bin/console doctrine:schema:validate I see this error in the console:
[Doctrine\ORM\Mapping\MappingException]
Invalid field override named 'user' for class 'ReusableBundle\Entity\Entry'.
Is there a workaround that I could follow to achieve the desired result?
Use trait to store shared properties
Override assotiation mapping or (possibly) attributes mapping in the class that uses that trait
TL;DR You should change the access modificator from protected to private. Don't forget that you will not be able to directly manipulate the private property in a subclass and will need a getter.
The exception appears due to the bug (I believe, or a quirk of the behavior) in the AnnotationDriver.
foreach ($class->getProperties() as $property) {
if ($metadata->isMappedSuperclass && ! $property->isPrivate()
||
...) {
continue;
}
It skips all non-private properties for MappedSuperclass letting them to compose metadata on the subclass parsing. But when it comes to overriding the driver tries to do it at a MappedSuperclass level, it doesn't remember that the property was skipped, fails to find it in the metadata and raise an exception.
I made a detailed explanation at the issue. You can find there also the link to the unit tests that highlight the case.
You'll have to try this in your own code to see, but it could be possible.
As an experiment, I overridden a trait in a class, then checked for the trait using class_uses() http://php.net/manual/en/function.class-uses.php
<?php
trait CanWhatever
{
public function doStuff()
{
return 'result!';
}
}
class X
{
use CanWhatever;
public function doStuff()
{
return 'overridden!';
}
}
$x = new X();
echo $x->doStuff();
echo "\n\$x has ";
echo (class_uses($x, 'CanWhatever')) ? 'the trait' : 'no trait';
This outputs:
overridden!
$x has the trait
Which you can see here https://3v4l.org/Vin2H
However, Doctrine Annotations may still pick up the DocBlock from the trait proper rather than the overridden method, which is why I can't give you a definitive answer. You just need to try it and see!
I had a similiar problem and solve it by override the property it self:
use UserAwareTrait;
/**
* #var EntrantInterface
* #ORM\ManyToOne(targetEntity="ReusableBundle\Model\EntrantInterface"inversedBy="entries")
*/
protected $user;
I want to use an abstract base class with common functionality for factories to extend, which works, but I don't know how to accurately specify the return type and have it detected by PHPStorm.
Here's an example. Is there a way I can document in PHPDoc that AppleFactory::make() returns AppleInterface and OrangeFactory::make() returns OrangeInterface?
<?php
namespace App\Factories;
abstract class AbstractFactory {
/** #var array $drivers */
protected $drivers;
/**
* instantiate the driver based on the given driver identifier
* #param string $driver Driver identifier.
* #return ???
* #throws UnknownDriverException If driver string is not in list of available drivers.
*/
public function make($driver) {
$class = $this->className($driver);
if (is_null($class))
throw new UnknownDriverException($driver);
return new $class;
}
/**
* get the full class name for the driver
* #param string $driver String mapping of class.
* #return string
*/
public function className($driver) {
return isset($this->drivers[$driver]) ? $this->drivers[$driver] : null;
}
}
class AppleFactory extends AbstractFactory {
protected $drivers = [
// both implement AppleInterface
'fuji' => \App\Apples\Fuji::class,
'gala' => \App\Apples\Gala::class
];
}
class OrangeFactory extends AbstractFactory {
protected $drivers = [
// both implement OrangeInterface
'navel' => \App\Oranges\Navel::class,
'blood' => \App\Oranges\Blood::class
];
}
Is there a way I can document in PHPDoc that AppleFactory::make() returns AppleInterface and OrangeFactory::make() returns OrangeInterface?
Based on your requirements above - a standard #method should do the job -- needs to be placed in PHPDoc comment for that class (AppleFactory and OrangeFactory accordingly). Something like this:
#method AppleInterface make($driver)
At the same time, since you do pass parameter to a factory method .. and returned instance has strong relation to that -- have a look at Advanced Metadata support in PhpStorm (IDE specific functionality). This is what Laravel IDE helper (for example) uses to provide better IDE integration with this framework.
More on this: https://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Advanced+Metadata
Hi can I ask about this in laravel framework
namespace Illuminate\Support\Facades;
/**
* #see \Illuminate\Auth\AuthManager
* #see \Illuminate\Contracts\Auth\Factory
* #see \Illuminate\Contracts\Auth\Guard
* #see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
}
what does the return 'auth' exactly returning to the caller ? is it text 'auth' or an object ? and what is the reason why they only have one method in that class ? I apologize i am just learning oop.
Thank you in advance.
In this case as you see method getFacadeAccessor it's returning auth string.
Facades are just "shortcuts" to use other classes but in fact you shouldn't use them everywhere if you don't need to.
In Laravel you can bind objects/classes into Application. So you can write for example:
$app->bind('something', function() {
return new SomeObject();
});
Let's assume there is method doSomething in SomeObject class.
Now you can use this method using:
$app['something']->doSomething();
But you can also create facade:
class GreatClass extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'something';
}
}
and now anywhere in your application you could use:
GreatClass::doSomething();
Answering your question this getFacadeAccessor is returning only the name the name of object that is used when bound to Application. To know how it's used you can look into the source of:
/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
The method you should look first is getFacadeRoot - because this method is returning the requested object.
Is there a way to extend classes auto-generated from database by Doctrine2 ?
Example: I have this User class generated by Doctrine.
<?php
namespace Entities;
/**
* User
*/
class User
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
/**
* #var string
*/
private $lastName;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*
* #return User
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* #param string $lastName
*
* #return User
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
I would like to add this function :
public function getFullName()
{
return $this->getFirstName().' '.$this->getLastname();
}
Is there a cleaner way than adding it directly into this class?
I tried to create another class (Test) in libraries and extends it, then add it in autoload (which is working), but i get an error when I try to save object :
class Test extends Entities\User {
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
Message: No mapping file found named 'Test.dcm.yml' for class 'Test'.
I'm using Doctrine2 in CodeIgniter3.
Thanks.
As explained in the Doctrine 2 FAQ:
The EntityGenerator is not a full fledged code-generator that solves all tasks. [...] The EntityGenerator is supposed to kick-start you, but not towards 100%.
In plain English this means you ask Doctrine to generate the Entity files only once. After that, you are on your own and do whatever changes you like (or it needs) to them.
Because an Entity is not just a container for some properties but it's where the entire action happens, this is how the flow should happen, Doctrine cannot write more code for you.
The only way to add functionality to the stub Entities generated by Doctrine is to complete the generated classes by writing the code that implements the functionality of each Entity according to its role in your Domain Model.
Regarding the other issue, on the Test class, the error message is self-explanatory: any class passed to the EntityManager for handling needs to be mapped.
Take a look at the help page about Inheritance Mapping. You can either map class User as a Mapped Superclass (it acts like a template for the derived classes and its instances are not persisted in the database) or you can use Single Table Inheritance to store the instances of all classes derived from User in a single table (useful when they have the same properties but different behaviour).
Or, in case you created class Test just because you were afraid to modify the code generated by Doctrine, put the behaviour you need in class User and drop class Test.
Seems you are having trouble while accessing the user entity class. You mentioned that test is a library class. Why not try to access the User entity class from a controller. If can do this then may be something is wrong with the configuration of test file. Besides, you need to map you doctrine entity class properly. You can have a look here to learn about doctrine mapping using yml: http://doctrine-orm.readthedocs.org/en/latest/reference/yaml-mapping.html
you can do this:
<?php
namespace Entities;
/**
* User
*/
class User extends Test
{
//... and extends Test
}
or
<?php
namespace Entities;
/**
* User
*/
class User
{
//...
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
view more
Symfony 2 - Extending generated Entity class
http://www.theodo.fr/blog/2013/11/dynamic-mapping-in-doctrine-and-symfony-how-to-extend-entities/
http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html
Annotation allows you to specify repository class to add more methods to entity class.
/**
* #ORM\Entity(repositoryClass="App\Entity\UserRepository")
*/
class User
{
}
class UserRepository extends EntityRepository
{
public function getFullName() {
return $this->getFirstName().' '.$this->getLastname();
}
}
// calling repository method
$entityManager->getRepository('User')->getFullName();
Here's a link [http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html]
7.8.8. Custom Repositories
When I programmed in ASP.NET MVC, there was a neat pattern called Repository. I want to implment it in Codeigniter but I do not know how. Here is what I actually want:
$mock_repository = new MockRepository();
$mock_repository->add(new Item(‘title1′, ‘description1′, 1));
$mock_repository->add(new Item(‘title2′, ‘description2′, 2));
$mock_repository->add(new Item(‘title3′, ‘description3′, 1));
$controller = new Item_controller($mock_repository);
$items = $controller->get_items_by_user_id(1);
$this->_assert_equals(count($items), 2);
I am using TOAST for Unit Testing. So how do I instantiate a controller within a test? The test is of course, another controller itself.
From what I know, to create a Generic Repository Pattern like in C#, you need 2 things PHP 5.6 dosen't have:
Real Method Overloading.
Generic Interface or Generic Abstract Class in PHP.
Click here for more on Generic Repository Pattern in C#.
However you can still create pseudo method overloading in PHP with the help of magic method __call, and we can type little more code for the generic part of the pattern.
Note: Before creating this pattern in Codeigniter 3.0 you will need to create a table in the database, and create auto loader for folder application/libraries.
First we need to create Interface in application/libraries folder:
<?php
interface IRepository
{
public function getById($id);
public function select($columns);
public function delete($id);
}
Seconde we need to create Abstract Class implementing the Interface and extending the CI_Model to be able to use the Database librarie:
<?php
abstract class Base_repository extends CI_Model implements IRepository
{
/**
* This must be valid table name in the Database.
*
* #var string $table Name of the table.
*/
protected $table;
public function __construct()
{
parent::__construct();
}
/**
* Pseudo method overloading.
* It's called when method is not declared in the abstract class.
*
* #param string $name Name of the method
* #param mixed $arguments Arguments of the method
*/
public function __call($name, $arguments)
{
switch ($name)
{
case 'save':
if ($arguments[0]->id > 0)
{
$this->update($arguments[0]);
}
else
{
$this->insert($arguments[0]);
}
break;
}
}
/**
* Get row with id.
*
* #param integer $id
* #return mixed
*/
public function getById($id)
{
return $this->db->get_where($this->table, ['id' => $id])->row_array();
}
/**
* Select columns.
*
* #param array $columns
* #return mixed
*/
public function select($columns = ['*'])
{
$this->db->select($columns);
return $this->db->get($this->table)->result();
}
/**
* Insert data.
*
* #param object $item
* #return void
*/
private function insert($item)
{
unset($item->id);
$this->db->insert($this->table, $item);
}
/**
* Update data.
*
* #param object $item
* #return void
*/
private function update($item)
{
$this->db->where('id =', $item->id);
unset($item->id);
$this->db->update($this->table, $item);
}
/**
* Delete data.
*
* #param integer $id
* #return void
*/
public function delete($id)
{
$this->db->delete($this->table, ['id' => $id]);
}
}
Third test the repository. Make a new model in application/model, and extend Base_repository, set table name and overload save method, create entity for this model:
<?php
/**
* The entity class.
*/
class Test
{
public $id;
public $info;
}
class Test_model extends Base_repository
{
/**
* Tell what table we are using.
*/
public function __construct()
{
parent::__construct();
$this->table = 'test';
}
/**
* "Overload" save method and call it from the parent.
*
* #param test $item Make use of the Dependency Injection.
* #return void
*/
public function save(Test $item)
{
parent::save($item);
}
}
Try it in the controller. Load the model and try to get, insert, ect...
To create real models is the same procedure. If you need to add more methods that will be the same for every model add them in the abstract class if you need to create methods only for specific model add it only in this model.
I don't recommend Codeigniter freamwork. Here are some patterns for PHP CLICK!
You would have to completely hijack the system files to load a controller from another controller. It can't be done, methinks.
It can be done with HMVC.
$result = Modules::run('controller/get_items_by_user_id', $params);
$this->_assert_equals($result, $expected);