Laravel 4 unittest using wrong database - php

I have created a testing database environment as follows:
return array(
'default' => 'sqlite',
'connections' => array(
'sqlite' => array(
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
),
)
);
When I initialise my database in my setUp function this all appears to work fine:
Mail::pretend(true);
Artisan::call('migrate');
$this->seed();
Any query directly in my test directly in my test class returns what I would expect however if I call a function on my model that does anything with the database it uses my 'live' (I'm running this in a vagrant dev server so no risk of ruining anything) database instead of my test one. Do I need to change my configuration further to ensure it is using my test database? Or do I need to instantiate my model in a special way?
An example of what doesn't work. In my test:
// gets the correct company
$comp = Companies::find(1);
// gets results from wrong database
$comp->quotesAndOrders();
where quotesAndOrders does a simple query on a hasMany relationship (orders)
$this->orders()->get();

First of all on our .env file we have to add a new variable.
looks like this.
DB_CONNECTION=sqlite
That's because of Laravel defaults to MySQL when this variable is absent from .env file:
see here..
'default' => env('DB_CONNECTION', 'mysql')
Now that our app is pointing into our sqlite database we can now run our migrations and seeding. After that if you just want to keep running MySQL remove DB_CONNECTION=sqlite from the .env file.
Now on your root folder you have a phpunit.xml file, open it and a new variable under <php> node
<env name="DB_CONNECTION" value="sqlite"/>

This project is based on someone elses code originally. In my companies model (annoyingly no other class and this was the first one I tested otherwise I would have spotted it sooner) there is an explicit definition to use the mysql database. I'm not sure why this was. I have removed that line and it now works.

Related

How to change Laravel's Job Batching table name and connection

Laravel 8 introduced Job Batching, which allows to execute jobs in batches and perform actions on batch completion. However Laravel documentation does not have a section regarding configurations of Job Batching table and database connection.
How can different database for job_batching table can be specified, and is it possible to rename the job_batching table name?
Open queue.php file in the config/ folder.
You can specify custom database and table name by editing/adding the batching key:
return [
/* Default Queue Connection Name */
'default' => env('QUEUE_CONNECTION', 'sync'),
...
// ADD THIS SECTION
'batching' => [
'database' => '<custom_database>',
'table' => '<custom_job_batching>',
],
...
];

How to use orchestral/tenanti in Laravel 5 to build a multi tenant application with multiple databases?

I am trying to build and application using Laravel 5. It is supposed to be a multi tenant database architecture using multiple databases. My employer requires this for security purposes.
I have tried manually managing the main DB migrations and the Tenant migrations but failed. So I decided to take the help of a Laravel specific package which is supposedly what I require.
Tenanti provides a way to have my purpose solved but the problem is that me being a novice developer, am not able to fully understand how to use it in my application.
I have installed it correctly I believe doing:
composer require "orchestra/tenanti=~3.0"
Adding these providers and aliases in the config app file:
'providers' => [
// ...
Orchestra\Tenanti\TenantiServiceProvider::class,
Orchestra\Tenanti\CommandServiceProvider::class,
],
'aliases' => [
'Tenanti' => Orchestra\Support\Facades\Tenanti::class,
],
Finally publishing the config and tweaking it according to the documentation for multiple databases:
php artisan vendor:publish
return [
'drivers' => [
'user' => [
'model' => App\User::class,
'migration' => 'tenant_migrations',
'path' => database_path('tenanti/user'),
],
],
];
At this point I am still blurry what to do next?
My doubts are as follows:
Where will the migration files be generated and stored? I mean there are two kinds of databases in my application obviously. One set of files is for the main DB which will store all the tenant information and the other files will be for the tenant DB. So how and where will these be stored?
I see the word 'driver' a lot in the documentation but I am not sure what driver is exactly.
How will I handle the authentication for the application? I mean whenever a tenant logs in, I will have to make sure the connection to the database changes dynamically. How will I accomplish this?
I tried to go through the repository of the package itself and make sense of the code inside but in vain. I am not very good when it comes to design patters like facades, command bus, service provider and so on, which is why I am not able to understand the flow of the package or make sense of it.
I tried to run some of the artisan commands which come with the package like:
php artisan tenanti:install {driver}
php artisan tenanti:make {driver} {name}
But I am getting an error like so:
[InvalidArgumentException] Database connection
[tenants] is not available.
Where can I find the resources to understand how to proceed with this?
+1 to #morphatic answer, it quiet accurate on most of the stuff.
Migration
One set of files is for the main DB which will store all the tenant information and the other files will be for the tenant DB. So how and where will these be stored?
For your main database you should be able to use the default database/migration and utilize php artisan make:migration and php artisan migrate.
Tenanti however will use the migration path set under the "driver" configuration. e.g:
'path' => database_path('tenanti/user'),
In this case the migration will be created/migrated from database/tenanti/user (you can choose other folder and it will use that folder). Once you set this up you can create new migration file for the user tenant via php artisan tenanti:make user create_blogs_table (as an example) and run migration via php artisan tenanti:migrate user (see the similarity between Laravel migration command and Tenanti?).
Driver
Driver is just the grouping of a tenant, you maybe grouping it by users, companies, or team etc. And there is possibility that you may require more than one type of group per project, otherwise most of the time you only be using single "group" or "driver".
Authentication or Accessing DB
How will I handle the authentication for the application? I mean whenever a tenant logs in, I will have to make sure the connection to the database changes dynamically. How will I accomplish this?
First of all, you need to consider how you're planning to distinguish each tenant. Most of the time I would see people tend to opt for subdomain. So in this case you need to check if the subdomain belongs to any of the user (by querying the main database) using a middleware and then connect to the database that belongs to the user.
Tenanti doesn't manage that part of the process, because everyone has different style on that aspect, but we do provide a code to dynamically connect to your database tenant from a base database configuration.
Let say you have the following config:
<?php
return [
'fetch' => PDO::FETCH_CLASS,
'default' => 'primary',
'connections' => [
'primary' => [
//
],
'tenants' => [
'driver' => 'mysql',
'host' => 'dbhost', // for user with id=1
'username' => 'dbusername', // for user with id=1
'password' => 'dbpassword', // for user with id=1
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
],
],
'migrations' => 'migrations',
'redis' => [ ... ],
];
You can follow the step available in https://github.com/orchestral/tenanti#multi-database-connection-setup and add the following code.
<?php namespace App\Providers;
use Orchestra\Support\Facades\Tenanti;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Tenanti::setupMultiDatabase('tenants', function (User $entity, array $template) {
$template['database'] = "tenant_{$entity->getKey()}";
return $template;
});
}
}
This would ensure that you be using tenant_1 database for user=1, tenant_2 database for user=2 and so on.
So how does Tenanti detect which user if active?
This is where you need to add logic in your middleware.
$user = App\User::whereSubdomain($request->route()->parameter('tenant'))->first();
Tenanti::driver('user')->asDefaultDatabase($user, 'tenants_{id}');
I've never used this package, but using the code you submitted above here's what I think is probably close to the right solution. You will probably still need to play with some of these values to get them correct:
Migration Paths
Since you're using the multi-database configuration, I believe you should be able to keep your migrations in the normal location, i.e. database/migrations. Tenanti will then create an exact replica of the database for each tenant in a different database. However, when you run php artisan tenanti:install user it might actually create a folder under database/ that indicates where you should put your migrations.
What is a "driver"?
The driver describes whether Tenanti will use a single or multiple databases, what models to use for determining different tenants, and where to store migrations. It is what you identified in the Tenanti config file you used above.
Database Connection Selection
You need to update config/database.php as follows. In a normal Laravel app, you would have the DB connection setup as follows:
<?php
return [
'fetch' => PDO::FETCH_CLASS,
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
'sqlite' => [ ...DB connection info... ],
'mysql' => [ ...DB connection info... ],
'pgsql' => [ ...DB connection info... ],
'sqlsrv' => [ ...DB connection info... ],
],
'migrations' => 'migrations',
'redis' => [ ... ],
];
However, in the case of Tenanti multi-database setup, you need to add in different connection info for each tenant's database. To do this you would add a new level to your database.php config file (this example assumes you're using mysql, but you could use any DB, or even different database engines for different tenants):
<?php
return [
'fetch' => PDO::FETCH_CLASS,
'default' => env('DB_CONNECTION', 'mysql'),
'connections' => [
'tenants' => [
'user_1' => [
'driver' => 'mysql',
'host' => 'dbhost', // for user with id=1
'database' => 'dbname', // for user with id=1
'username' => 'dbusername', // for user with id=1
'password' => 'dbpassword', // for user with id=1
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'user_2' => [
'driver' => 'mysql',
'host' => 'dbhost', // for user with id=2
'database' => 'dbname', // for user with id=2
'username' => 'dbusername', // for user with id=2
'password' => 'dbpassword', // for user with id=2
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
],
],
'migrations' => 'migrations',
'redis' => [ ... ],
];
As you can see, each tenant has its own database instance that can be located on a different host and have a different username/password. Tenanti needs to be told how to figure out which database to use. This is what the documentation on Database Connection Resolver describes. In their example, they've named their tenant databases using acme_{$user->id} whereas in my example above I used user_{$user->id}.
Like I said, I've never actually set this up myself, but these are my best guesses based on the docs, and having used other packages by this same developer. Hope this helps!

Testing Yii REST application using a second database

I wrote an API using Yii2 and following the REST guide. My API is working and I want to write some tests for it, so I once again followed the guide on how to run tests and got unit tests working. I then looked around Codeception documentation about testing WebServices and got this working too.
My problem is that API calls are not using my test database. I have two databases, one called db and the other testdb. Here is my config.php file in tests/codeception/config/:
return [
'components' => [
'db' => [
'dsn' => 'mysql:host=localhost;port=8889;dbname=testdb;unix_socket=/Applications/MAMP/tmp/mysql/mysql.sock',
],
'mailer' => [
'useFileTransport' => true,
],
'urlManager' => [
'showScriptName' => true,
],
],
];
I wrote a simple test that send a GET request to an endpoint to retrieve data. My test database is empty so I am expecting to receive an empty response, but I get the content of my other database instead.
I then tried to set YII_ENV to test as described in the Environment Constant section here so that I could test against the env variable YII_ENV_TEST and change the db configuration accordingly. I tried to set this variable in the _bootstrap.php file in the tests/codeception/ folder:
defined('YII_ENV') or define('YII_ENV', 'test');
I then logged the value of YII_ENV in the web/index.php file (index-test.php is not called, might be a problem too), and it is undefined.
What am I doing wrong? I tried including the Yii2 module in my api.suite.yml file but requests don't have return code anymore if I do that, it returns N/A. Is there another way to change which database Yii should use?
You can make an test_config.php file and at the end of the config place this
if (file_exists('protected/config/test_config.php'))
{
include 'test_config.php';
}
the file will be included if it exists. And the file test_config.php should contain the overwritten value for the db connection.
Hope this helps!
Keep on coding!
Ares.
Well I found a "solution" by using this other app template: https://github.com/githubjeka/yii2-rest
The file organization fits my needs better and I can easily configure which database to use.

Laravel Migrations - Table Prefix Issue

I'm building a dummy site to test Laravel 3.x.
I'm creating my site migrations right now. Everything was doing just fine until the following error showed up:
SQLSTATE[42s02]: Base table or view not found: 1146 Table 'databasenamehere.prefix_laravel_migrations' doesn't exist
The issue is that laravel all of a sudden started to prefix the 'laravel_migrations' table (when it is supposed to do it only with the other ones).
I wonder if I'm doing something wrong or if it is a known issue.
I'm trying to run the following migration (using the php artisan migrate application command):
public function up()
{
Schema::create('siteinfo', function ($table)
{
$table->engine = 'InnoDB';
$table->string('name');
$table->string('title')->nullable();
$table->string('corp_name')->nullable();
$table->string('corp_addr')->nullable();
$table->string('corp_phone')->nullable();
$table->string('corp_city')->nullable();
$table->string('corp_state')->nullable();
$table->string('corp_email')->nullable();
$table->string('main_url')->nullable();
$table->timestamps();
});
}
Any help would be great.
EDIT 1:
I noticed some minutes ago that my tables got no prefix at all, even with the "prefix" configuration set correctly on the config/database.php file.
Everything works fine if I remove the prefix. I know that I can set the prefix manually in every migration I run, but well...
In application->config->database.php set the prefix as follows
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'foodb',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'prefix' => 'ula_', <-- this is where you need to set the table prefix
),
After setting this, migrate:reset and migrate it again
I have done this way and its works perfect
On Laravel 5.4.*, I ended up creating the artisan command to add table prefix on certain tables (not globally) using below handle method.
public function handle()
{
$this->tablePrefix = 'tmp_';
// Set table prefix
DB::setTablePrefix($this->tablePrefix);
$data = [
'--path' => [
'database/prefixed-migrations' // Directory Path to migrations which require table prefix
],
'--database' => 'cli',
'--force' => true
];
$this->call('migrate', $data); // Next call the migration
Model::reguard();
}
Hope this helps if anyone looking to prefix on certain tables without setting it globally.

Setup CakePHP with an existing Database

I have a MySQL server database currently setup which has a few simple tables to track orders, such as tblOrders, tblUsers, tblProducts.
Although I have a website working with it fine now, I'd like to setup CakePHP to act as the server side framework for handling interaction with the database rather than using hand written queries in my PHP pages.
Is there a simple way to setup CakePHP with my existing Database/tables?
If I understand correctly, I will have a main MyApplication class which Extends Controller, as well as Order, User, Product, (... other tables) classes which each extend the MyApplication class.
It looks like the REST guide uses a method in the configuration file called Router::mapResources('recipes');. Will this create the controllers for each table with the default methods to use for REST?
I.e., in the /app/config/routes.php configuration file:
// /app/config/routes.php
Router::mapResources('tblOrders');
Router::mapResources('tblUsers');
Router::mapResources('tblProducts');
// /app/config/database.php
<?php
class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/myStoreDB',
'persistent' => false,
'host' => 'localhost',
'login' => 'admin_user',
'password' => 'c4k3roxx!',
'database' => 'main_db',
'prefix' => ''
);
}
If you want Model, Controller and View code created for you, you're looking for baking.
Cake expects your database tables to follow a naming convention. I'm not sure how the bake will go because your tables don't match this convention. You may need to create your model classes by hand and specify the useTable attribute on each class. For example:
<?php
class Order extends AppModel {
public $useTable = 'tblOrders';
}
Once this is done, I expect the baking of Controllers and Views should work as normal. Note that with mapResources you still need Controller code. That function just generates routes.
If Cake is the only thing that will be touching this database, I recommend renaming tables and columns in line with the conventions it expects.

Categories