Multitenant Saas built in yii2 - yii2

I am developing a multi-tenant LMS in yii2. All the tenants will have their respective user accounts. But to login to the system those user should also exists in main database, user table. So do I need to replicate the user accounts in both databases.

Instead of replicate the users table You could simply create a common table for user .. and manager the access to the common db redifining the tableName() function ..
class MyUserClass extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'common_database.' 'user_table';
}
....
class MyTenancyModel extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'my_tenancy_table';
}

Related

How would i orderBy my query based on values from a diffrent tabel that it is in a relationship with

i have 2 tabels one being vehicles and another being road tax.
my 'vehicles' tabel has an id & registration field which is in relationship with my 'road tax' tabel which has id, vehicle_id, vaild from & an expires field. i have a one to many relationship as my vehicles will have had many years history of when i taxed them
i need the most simple way to list all my vehicles in order of which will need to be re-taxed first.
the closest i have is getting my vehicles to list when the tax is due to expire. i am really strugling to get them in the order i need. i have a basic understanding of php and mysql so hoping someone can shine a light on where i need to focus. i thought i could either just orderBy the expires colum, just like how i can successfully orderBy registration. is this because my expires field originate from a realtionship table?
controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Road_tax;
use App\Models\Vehicle;
use Carbon\Carbon;
class DashboardController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function Index()
{
$road_taxes = Vehicle::with('latest_Road_Tax')->get()
return view('dashboard.index', compact('road_taxes'));
}
}
Vehicle Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Vehicle extends Model
{
public function Road_taxes()
{
return $this->hasMany(Road_tax::class);
}
public function latest_Road_Tax()
{
return $this->hasOne(Road_tax::class)->latest("expires");
}
}
View
#foreach($road_taxes as $road_tax)
<div class="dashboard-item-title">
<h6 style="font-weight:600; margin-bottom:0px;">{{$road_tax->registration}}</h6>
<span class="dashboard-item-body" style="margin-top:-10px;">
<small style="font-weight:300; color:grey;">Tax expires for this vehicle on</small>
<small style="font-weight:300"> | {{$road_tax->latest_Road_Tax->expires}}</small>
</span>
</div>
#endforeach
You can do is a with() method and pass as a query builder.
So basically you only need 1 relationship vehicle->hasMany(Road_tax::class)
Your model should be:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Vehicle extends Model
{
public function road_taxes()
{
return $this->hasMany(Road_tax::class);
}
}
So if you want every vehicle to list their latest road tax
You can query this using with()
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Vehicle;
use Carbon\Carbon;
class DashboardController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function Index()
{
$road_taxes = Vehicle::with([
'road_taxes' => function ($query) {
$query->lastest()->limit(1);
}
])->get();
return view('dashboard.index', compact('road_taxes'));
}
}
This method will list 1 road taxes associated to the vehicle
EDITED
This is the where you only get the vehicle with road road taxes
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Vehicle;
use Carbon\Carbon;
class DashboardController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function Index()
{
$road_taxes = Vehicle::with([
'road_taxes' => function ($query) {
$query->lastest()->limit(1);
}
])->has('road_taxes')->get();
return view('dashboard.index', compact('road_taxes'));
}
}

The table does not exist: {{%active_record}}

I am new to yii and learing generating the CRUD operation through gii first i create a country table and generate the model and controller and views using gii i am get following error
Invalid Configuration – yii\base\InvalidConfigException The table does not exist: {{%country_search}}
Add this to CountrySearch class:
/**
* #inheritdoc
*/
public static function tableName()
{
return 'country';
}
Just adding to temirbek answer:
file models/Country.php
<?php
namespace app\models;
use yii\db\ActiveRecord;
class Country extends ActiveRecord
{
public static function tableName()
{
return 'country';
}
}

Laravel Settings Service Provider Database Migration

I created the settings table via a migration and implemented a SettingsServiceProvider (based on this post Laravel - Set global variable from settings table). Now it says that the base table can't be found. Of course not, because I need to run the migration first.
[Illuminate\Database\QueryException] SQLSTATE[42S02]: Base table or view not found: 1146 Table 'db.settings' doesn't exist (SQL : select value, name from settings)
Any ideas what to do? I course I could create the table manually but this would destruct the ideas of migrations...
Any help would be appreciated. I'm using Laravel 5.3.
Update 25/11/2016 - added code snippets
Service Provider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
config()->set('settings', \App\Setting::pluck('value', 'name')->all());
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
Migration
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateSettingsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('settings', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('settings');
}
}

Creating a one-to-many relationship in Yii2

Let's say we have two entities: User and Post.
In my understanding, in order to have a one-to-many relationship between User and Post, you need to do the following:
class User {
...
public function getPosts()
{
return $this->hasMany(Order::className(), ['user_id' => 'id']);
}
}
class Post {
...
public function getUser()
{
return $this->hasOne(Order::className(), ['id' => 'user_id']);
}
}
Is this right? Is there anything else I need to add in order to make everything work? The Yii2 documentation is not very clear to me.
Yes, this is enough (except that you inserted Order class name instead), however it's also recommended to add PHPDoc for relations:
User model:
/**
* ...
*
* #property Post[] $posts
*/
class User
{
/**
* #return \yii\db\ActiveQuery
*/
public function getPosts()
{
return $this->hasMany(Post::className(), ['user_id' => 'id']);
}
}
Post model:
/**
* ...
*
* #property User $user
*/
class Post
{
/**
* #return \yii\db\ActiveQuery
*/
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
}
Then when you will call $user->posts or $post->user you will get full autocomplete if you are using IDE. It's also useful because you can see the relation list just by looking at the top of the file, because relations accessed as virtual properties, $user->getPosts() call will return yii\db\ActiveQuery object and not \yii\db\ActiveRecord array. It's better to separate them with linebreak from model attributes (they are also added for autocomplete and seeing the structure of according database table without looking at database).
By the way, if you generate model with Gii, if you specified foreign keys correctly, relations and PHPDoc will be generated automatically.
Note that if you don't need to use $post->user, you can omit user relation declaration in Post model. You can declare relations that are only needed for usage.

How to login from different tables in Yii2 basic application?

i have two table : Admin and Employees.
with different columns name.
how to create database login system for this scenario.
i am using Yii2 basic application.
i don't know how to deal with this problem.
One way out of this situation is using abstractions
E.g:
abstract class User extends ActiveRecord{
// abstract method that should overrided child classes
abstract function tableName();
}
class Admin extends User {
function tableName() { return 'admin'; }
}
class Employee extends User {
function tableName() { return 'employee'; }
}
But, it's depended on your exact situation. Also you can use setTableName() method for override only table name in the same class.
I agree that it's better to use some common user table. But if you still want to use 2 tables you can do that like this
<?php
namespace common\models;
use Yii;
use yii\base\Model;
/**
* Login form
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
/**
* #inheritdoc
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*/
public function validatePassword()
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user) {
$this->addError('password', 'Incorrect username or password.');
return ;
}
if (!$user->validatePassword($this->password)) { // you need to implement this method for both models
$this->addError('password', 'Incorrect username or password.');
return ;
}
}
}
/**
* Logs in a user using the provided username and password.
*
* #return boolean whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
} else {
return false;
}
}
/**
* Finds user by [[username]]
*
* #return User|null
*/
public function getUser()
{
if (!$this->_user) {
$this->_user = Employee::findByUsername($this->username);
}
if (!$this->user) {
$this->_user = Admin::findByUsername($this->username);
}
return $this->_user;
}
}