How to trigger Eloquent Manager in Slim 3.1 Dependency Injection - php

I have write my code to instantiate Eloquent Capsule/Manager using slim DI like this
$container['db'] = function ($c) {
$settings = $c->get('database');
$db = new \Illuminate\Database\Capsule\Manager;
$db->addConnection($settings);
$db->setAsGlobal();
$db->bootEloquent();
return $db;
}
And I have my route like this
$app->get('/adduser', function() {
$user = new Users;
$user->name = "Users 1";
$user->email = "user1#test.com";
$user->password = "My Passwd";
$user->save();
echo "Hello, $user->name !";
});
When I run the route in browser it will produce error in web server error log
PHP Fatal error: Call to a member function connection() on a non-object in /home/***/vendor/illuminate/database/Eloquent/Model.php on line 3335
In my opinion this is happened because the Eloquent Capsule/Manager is not triggered to be instantiate by DI.
I found a solution to solve this by declare the Model with custom constructor like this
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Capsule\Manager as Capsule;
class Users extends Eloquent {
protected $table = 'users';
protected $hidden = array('password');
public function __construct(Capsule $capsule, array $attributes = array())
{
parent::__construct($attributes);
}
}
But I don't think this is a clean solutions, because I have to rewrite all my Models using custom constructor.
I need help to find solutions for my problem.
I try to use code below:
$app->get('/adduser', function() use ($some_variable) {
// create user script
});
but so far I don't know how to trigger $container['db'] using this method. I really appreciate a help here.

It's probably not a good idea to inject your capsule manager into each model.. As you say yourself, that's going to be a pain to manage.
Have you tried this code outside of the closure? ie. in the bootstrap part of your app..
$db = new \Illuminate\Database\Capsule\Manager;
$db->addConnection($settings);
$db->setAsGlobal();
$db->bootEloquent();
The setAsGlobal function makes the Capsule Manager instance static, so the models can access it globally.
Just to note, convention is to name your model classes in singular form. ie. User rather than Users.

Related

Using Illuminate DB outside of Laravel, can't use DB::table()

I have installed some Illuminate packages to use outside of Laravel. However I can't use any of the DB methods.
For example:
$this->query = DB::table((new Vehicle())->getTable())->query();
The above gives me this error:
Error: RuntimeException: A facade root has not been set. in /var/www/vendor/illuminate/support/Facades/Facade.php:218 Stack trace: #0 /var/www/app/Services/PickCarouselService.php(37): Illuminate\Support\Facades\Facade::__callStatic() #1
Within my Ide I get the following message on ::table:
Method 'table' not found in \Illuminate\Support\Facades\DB
I can use models and everything just fine, just not DB directly, how can I fix this?
Edit 1
How would I create my own instance of the DB facade? We do have the following, would it be something similar? If so, what would I pass into the DatabaseManager for the Factory?
$app->instance(Database::class, (new Database($app))->boot()->getConnection());
$app->alias(Database::class, 'database');
Edit 2
Don't think i'm doing this correctly as not too sure what I would pass in for the app.
$app = new Application();
$app->instance(Database::class, new DatabaseManager(null, new ConnectionFactory($app)));
$app->alias(DatabaseManager::class, 'db');
// Application
<?php
namespace App;
use Illuminate\Container\Container;
class Application extends Container
{
protected $basePath = '/var/www';
public function __construct()
{
$this->bindPathsInContainer();
$this->registerBaseBindings();
}
...
Managed to figure out how to create a custom facade. But nice if I could use it like DB::table, but this works:
$app = new Application();
$app->singleton('db.factory', function ($app) {
return new ConnectionFactory($app);
});
$app->singleton('db', function ($app) {
return new DatabaseManager($app, $app['db.factory']);
});
$this->query = app()->db->table('tablename');

How to mock Auth in a Laravel 5.2 Model

I'm trying to write some unit tests for a brand new mini app. I usually write functional tests so this is me branching out to try and do it properly with mocking and stubs and all those things that make it just about the code.
The model looks like this :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Auth;
class myModel extends Model
{
public static function getUser()
{
$user = \Auth::user();
return $user->adldapUser->cn[0];
}
}
And the test :
class MyModelTest extends TestCase
{
public function testGetUser()
{
$mockResult = new StdClass();
$mockResult->adldapUser = new stdClass();
$mockResult->adldapUser->cn=array("test");
$auth = $this->getMock('Auth');
$auth
->method('user')
->will($this->returnValue($mockResult));
$this->assertEquals('test',\App\MyModel::getUser());
}
}
But when I run the unit test I get the following error message :
There was 1 error:
1) MyModelTest::testGetUser ErrorException: Trying to get property of
non-object
/home/aidan/web/vagrant-web-dev/src/apps/orcid/app/MyModel.php:61
/home/aidan/web/vagrant-web-dev/src/apps/orcid/tests/MyModelTest.php:18
and if I post out $user it's NULL.
What am I doing wrong here?
Here I go again answering my own question half an hour after asking it.
For prosperity's sake I'll leave it here.
The answer was in this post here : https://stackoverflow.com/a/17602763/808124
So I replaced the $this->getMock with
Auth::shouldReceive('user')->once()->andreturn($mockResult);
Working
I use it this way:
$user -factory(User:class)->create();
\Auth::shouldReceive('guard')->andReturnSelf()
->shouldReceive('user')->andReturn($user)
->shouldReceive('check')->andReturn(true);

Laravel eloquent build child object from parent with mongo

I am using laravel 5.1 and jenssegers mongodb and I am having some issues with such structure
class ServiceProvider extends Eloquent {
protected $collection = 'service_provider';
protected $connection = 'mongodb';
public static function factory($serviceId) {
switch ($serviceId) {
case self::SERVICE_PROVIDER_CHILD_CARE : {
return new ChildCareServiceProvider();
}
break;
}
}
public static get_by_service_id($service_id) {
return self::find($serviceId)->first();
}
}
class ChildCareServiceProvider extends ServiceProvider implements IServiceProvider
{
protected $collection = 'service_provider';
protected $connection = 'mongodb';
public function availabilityTimes()
{
return $this->hasMany('App\Models\ServiceProvider\ServiceProviderAvailabilityTime');
}
}
When I am saving or updating service provider I know what kind of service it is , so I can use factory method to get child and save it. But when I am getting service by id - I dont know service type yet, I only know its id. So I do have a method in ServiceProvider which makes query to mongo collection and getting record by its id. In this case that record will be an instance of object ServiceProvider. Is there an easy way to create ChildCareServiceProvider object from ServiceProvider object data? I tried something like
$data = ServiceProvider::find($serviceId)->first()->attributesToArray();
$serviceProvider = new ChildCareServiceProvider($data);
but in this case $serviceProvider object internal structure is little bit different in terms of its internal properties, which somehow affects my availabilityTimes relationship
I am sorta new to laravel and mongo, any advice would be greatly appreciated
Take a look at PHP's magic methods. What you can do is set the entity and then for example the __get() and __set() methods, set/get the value from/to the entity.

Zend database adapter - register in bootstrap.php?

I am trying to register my default database adapter in my bootstrap.php file so that I can access it where ever I am. This is my code so far:
//bootstrap.php
protected function _initDb()
{
$dbAdapter = Zend_Db::factory(Zend_Registry::get('configuration')
->resources->db->adapter,
Zend_Registry::get('configuration')
->resources->db->params->toArray());
Zend_Registry::set('dbAdapter', $dbAdapter);
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
}
I am then trying to call it in one of my models by saying:
//exampleModel.php
$select = $this->_getDbAdapter()
->select()
->from(array('t' => $this->_getTable()->getName()),
array('name'))....
However I am just getting the error:
Call to undefined method Application_Model_Example::_getdbAdapter() in...
So obviously it is looking for it within my current class and can't find it...
You need this in your Model_Example
public function _getSqlAdapter()
{
return Zend_Registry::get('dbAdapter');
}
Or directly call Zend_Db_Table::getDefaultAdapter() instead of $this->_getDbAdapter()
In the code provided you don't appear to be calling it the adapter from the registry. You would need to use Zend_Registry::get('dbAdapter');
What class does Application_Model_Example extend?
I have Zend_Db_Table::setDefaultAdapter($dbAdapter); in my bootstrap (notice its Zend_Db_Table, not Zend_Db_Table_Abstract).
Then in my models, I would just use
$select = $this->->select()
->from(array('t' => $this->_getTable()->getName()), array('name'))....
assuming your model extends Zend_Db_Table?

MVC, DI (dependency injection) and creating Model instance from Controller

My Dispatcher is "choosing" correct Controller; then creating Controller's instance (DependencyInjectionContainer is passed to Controller constructor); then calling some Controller's method...
class UserController extends Controller
{
public function __construct(DependencyInjectionContainer $injection) {
$this->container = $injection;
}
public function detailsAction() {
...
}
}
DependencyInjectionContainer contains DB adapter object, Config object etc.
Now let's see what detailsAction() method contains...
public function detailsAction() {
$model = new UserModel();
$model->getDetails(12345);
}
As you see I'm creating new instance of UserModel and calling getDetails methods.
Model's getDetails() method should connect to db to get information about user. To connect to DB UserModel should be able to access DB adapter.
What is the right way to pass DependencyInjectionContainer to the UserModel?
I think that this way is wrong...
public function detailsAction() {
$model = new UserModel($this->container);
$model->getDetails(12345);
}
Instead of injecting the entire DI Container into your classes, you should inject only the dependencies you need.
Your UserController requires a DB Adapter (let's call this interface IDBAdapter). In C# this might look like this:
public class UserController
{
private readonly IDBAdapter db;
public UserController(IDBAdapter db)
{
if (db == null)
{
throw new ArgumentNullException("db");
}
this.db = db;
}
public void DetailsAction()
{
var model = new UserModel(this.db);
model.GetDetails(12345);
}
}
In this case we are injectiing the dependency into the UserModel. In most cases, however, I would tend to consider it a DI smell if the UserController only takes a dependency to pass it on, so a better approach might be for the UserController to take a dependency on an Abstract Factory like this one:
public interface IUserModelFactory
{
UserModel Create();
}
In this variation, the UserController might look like this:
public class UserController
{
private readonly IUserModelFactory factory;
public UserController(IUserModelFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory");
}
this.factory = factory;
}
public void DetailsAction()
{
var model = this.factory.Create();
model.GetDetails(12345);
}
}
and you could define a concrete UserModelFactory that takes a dependency on IDBAdapter:
public class UserModelFactory : IUserModelFactory
{
private readonly IDBAdapter db;
public UserModelFactory(IDBAdapter db)
{
if (db == null)
{
throw new ArgumentNullException("db");
}
this.db = db;
}
public UserModel Create()
{
return new UserModel(this.db);
}
}
This gives you better separation of concerns.
If you need more than one dependency, you just inject them through the constructor. When you start to get too many, it's a sign that you are violating the Single Responsibility Principle, and it's time to refactor to Aggregate Services.
I'd use a singleton object for all config parameters :
You set it up in your bootstrap, then choose to use it directly or pass it as parameter in your objects.
The idea being to have one method all around to retrieve your config data.
You may then provide an abstract class for db manipulation which uses your config. singleton.
DependancyInjection can still be used to override your default data.
The above link in the comment (possible 'duplicate') concludes on using constructor injection : this is close to your current method.
However if I try to figure how your model works, I guess you will have many other model classes other than "userModel". Thus an abstract class using a config singleton might be a good solution : all your next model classes will just extend this abstract class, and you don't have to worry about your config.
On the other hand, your solution is good to me as long as your dependanceInjectionContainer changes often.

Categories