简体   繁体   中英

Interface with DB Facade in Laravel

First, I apologize if this is a stupid question. I recently read an article about repository design pattern and I have a problem when making interface implementation for Laravel Query Builder (Illuminate\\Support\\Facades\\DB).

DatabaseService.php

use Modules\Core\Interfaces\IDatabase;
use \DB;

class DatabaseService implements IDatabase
{  
  protected $db;

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

  public function select($str)
  {
    $this->db::select($str);
    return $this->db;
  }

  public function table($tableName)
  {
    $this->db::table($tableName);
    return $this->db;
  }

  ...
}

IDatabase.php

<?php namespace Modules\Core\Interfaces;
interface IDatabase
{
  public function select($str);
  public function table($tableName);
  public function raw($rawQuery);
  public function transaction($callback);
  public function first();
  public function get();
}

CoreServiceProvider.php

...

public function register()
{
  ...
  $this->app->bind('Modules\Core\Interfaces\IDatabase', function($app) {
    $db = $app->make(DB::class);

    return new DatabaseService($db);
  });
  ...
}

MailboxRepository.php

<?php namespace Modules\Mailbox\Repositories;

use Illuminate\Database\Eloquent\Model;
use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;

class MailboxRepository implements IMailbox
{
  public function __construct(..., IDatabase $db)
  {
  ...
    $this->db = $db;
  }
  ...
  public function getBadges()
  {
    $badges = $this->db->table('mailbox as a')
              ->select($this->db->raw(
                "SUM(a.type = 'inbox') as inbox, 
                 SUM(a.is_read = 0 AND a.type = 'inbox') as unread,
                 SUM(a.type = 'sent') as sent,
                 SUM(a.type = 'draft') as draft,
                 SUM(a.type = 'outbox') as outbox,
                 SUM(a.type = 'spam') as spam,
                 SUM(a.type = 'trash') as trash,
                 SUM(a.is_starred = 1) as starred"
              ))
              ->first();

    return $badges;
  }
  ...
}

MailboxServiceProvider.php

<?php namespace Modules\Mailbox;

...
use Modules\Mailbox\Interfaces\IMailbox;
use Modules\Mailbox\Repositories\MailboxRepository;
use Modules\Core\Interfaces\IDatabase;
use Illuminate\Support\ServiceProvider;

class MailboxServiceProvider extends ServiceProvider 
{
  protected $defer = true;

  public function register()
  {
    $this->app->bind(IMailbox::class, function($app) {
      return new MailboxRepository(
        ..., $app->make(IDatabase::class)
      );
    });
  }

  public function provides()
  {
    return [IMailbox::class];
  }
}

With error message :

[2018-01-31 13:45:04] local.ERROR: Call to undefined method Illuminate\Support\Facades\DB::select() 
{"userId":1,"email":"info@narpandi.com","exception":"[object] 
(Symfony\\Component\\Debug\\Exception\\FatalThrowableError(code: 0): Call 
to undefined method Illuminate\\Support\\Facades\\DB::select() at 
/var/www/personal-
website/app/Modules/Mailbox/Repositories/MailboxRepository.php:86)

How to do this correctly? Thank you for your kind help.

I don't think this is a common repository pattern, in repository pattern you try to create methods like:

Object get(Object id);
void create(Object entity);
void update(Object entity);
void delete(Object entity);

Edit, try to do something like docs: Database

use Modules\Core\Interfaces\IDatabase;
use Illuminate\Support\Facades\DB;

class DatabaseService implements IDatabase
{  

  public function select($str, $args)
  {
    return DB::select($str, $args);
  }
}

But i say again this doesn't look like a repository .

After trial and error, I finally figure out how to do this. As mentioned in https://stackoverflow.com/a/26356144/3050636 , you cannot use facade DB directly so instead you need to explicitly pass class that is behind the DB facade.

In my case, I use bridge pattern and fluent interface (CMIIW) so I will provide two versions:

  • Without bridge pattern

MailboxServiceProvider.php

<?php namespace Modules\Mailbox;

use Modules\Mailbox\Interfaces\IMailbox;
...
use Modules\Mailbox\Repositories\MailboxRepository;
use Illuminate\Support\ServiceProvider;

/* Use these instead of DB facade */
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Connectors\ConnectionFactory;

class MailboxServiceProvider extends ServiceProvider 
{
  protected $defer = true;

  public function register()
  {
    $this->app->bind(IMailbox::class, function($app) {
      return new MailboxRepository(
        ..., new DatabaseManager($app, new ConnectionFactory($app))
      );
    });
  }

  public function provides()
  {
    return [IMailbox::class];
  }
} 

MailboxRepository.php

<?php namespace Modules\Mailbox\Repositories;

use Illuminate\Database\DatabaseManager;
use Modules\Mailbox\Interfaces\IMailbox;

class MailboxRepository implements IMailbox
{
  ...
  protected $db;

  public function __construct(..., DatabaseManager $db)
  {
    ...
    $this->db = $db;
  }
  ...
}
  • With bridge pattern and fluent interface

CoreServiceProvider.php

<?php namespace Modules\Core;

....
use Modules\Core\Services\DatabaseService;
use Modules\Mailbox\Repositories\MailboxRepository;
use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;

/* Use these instead of DB facade */
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Connectors\ConnectionFactory;

use Illuminate\Support\ServiceProvider;

class CoreServiceProvider extends ServiceProvider 
{
  public function register()
  {
    ...
    $this->app->bind(IDatabase::class, function($app) {
      return new DatabaseService(new DatabaseManager($app, new ConnectionFactory($app)));
    });

    $this->app->bind(IMailbox::class, function($app) {
      return new MailboxRepository(
        ..., $app->make(IDatabase::class)
      );
    });
    ...
  }
}

DatabaseService.php

<?php namespace Modules\Core\Services;

use Modules\Core\Interfaces\IDatabase;
use Illuminate\Database\DatabaseManager;

class DatabaseService implements IDatabase
{  
  protected $db;

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

  public function select($str)
  {
    $this->db = $this->db->select($str);
    return $this->db;
  }

  public function table($tableName)
  {
    $this->db = $this->db->table($tableName);
    return $this->db;
  }
  ...
}

MailboxRepository.php

<?php namespace Modules\Mailbox\Repositories;

use Modules\Core\Interfaces\IDatabase;
use Modules\Mailbox\Interfaces\IMailbox;

class MailboxRepository implements IMailbox
{
  ...
  protected $db;

  public function __construct(..., IDatabase $db)
  {
    ...
    $this->db = $db;
  }
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM