How to have different seeders per environments in Laravel 4? - php

I have a simple question but I haven't found an answer in the web. Maybe my keywords are false.
So I am developing an app in Laravel 4. And I need to seed the database with different values according to the current active environment.
So for example, if I am on the local environment, I want to have test data and so on. But when I am on the production environment I only want to have an admin user.
Does Laravel has an built in solution for that?
If not, how can check, which environment is active in the app/seeds/DatabaseSeeder.php file. So I can call different seeder according to the environment.

There's no built-in handler for different environments in the manner you would like.
Solution
Within the seeder class, you should be able to use App::environment() to detect the environment, and do logic based on that.
You can add that within each table seeder class, or within the DatabaseSeeder.php file:
public function run()
{
Eloquent::unguard();
if( App::environment() === 'development' )
{
$this->call('UserTableSeeder');
}
}
Alternatively
Consider adding multiple database connections within your app/config/database.php file. That way, instead of seeding per environments, you can populate databases from multiple connections within the same environment (and the environment can still change but have 2 or more separate db connections).
If that fits your use case, see my answer on multiple database connections here.

Related

Is it possible to use a model in it's own factory in a Laravel project?

My team is building a project using laravel for the server code. Early on we were going to use models to manage some of the images used in the project, but we didn't have the images from the client yet. So we built out the models and used a laravel factory and faker to create fake images for testing.
A month later (and many test cases later) we have the actual images from the client. I added the images to the project, created structural database seeders to populate the data needed for the database, and wrote out unit tests for the model to confirm it works.
The issue is that now some of the tests are failing because the factory we're using for the image model still uses factory and faker. Anywhere where we need to look for a specific file using the model we get fails from the fake data provide by faker.
I thought "well that's fine, I can just switch out the fake data in the factory for randomized data from the actual model". The problem I'm running into now is that when I try to use the actual model within the factory function, the model class only gets provided as a factory:
I know there's a good reason for this happening behind the scenes, I'm just wondering if there's a way of getting around it. If it's possible to use the actual model in the factory it would prevent me from having to rewrite a lot of test cases to swap out the factory for the actual model. It also seems like this would be a really convenient way of being able to do feature testing for items that you know will exist but don't have the actual assets for yet.
Is there a way around this or should I plan on buckling down and refactoring my tests?
A factory exists to generate a model on demand using fake data — not necessarily from faker but data that doesn't represent an entity that already exists. You are attempting to create factories that depend on the database having been populated with real data but if you have a database with real data that you need to test against then you should use that data directly. Your approach would give tests a dependency on real data which would now need to be distributed to all developers and build services.
Populating your database with a fixed set of data for testing should be done using Database Seeders. At the start of the development you should create a database seeder (which can use factories) to populate your database with fake data, then once you obtain the real data you can add an additional database seeder (or populate the database directly from outside of Laravel if necessary). This approach means that your application can be tested without any concern for whether the data is real or not, and you'd continue to be able to use factories as they're intended to be used.
The unfortunate conclusion here is that if you wish for your tests to be resilient you're going to need to refactor.
If you absolutely must use factories with real data then you can create your own faker provider that has fallbacks for if the data does not exist.
I believe if you declare the class object as the Faker class is being passed as a parameter within the factory declaration, you should be able to use it within the factory itself. I might be wrong though, I believe it happens that way because the factory itself is a function call, so any parameters used need to be declared before hand within its function() call.
If I recall correctly, if you do this:
$factory->define(App\models\AreaOfAffectMap::class, function(AOA $AOA) {...
It should work.

What's the right way to unit test a read-only model in Laravel?

I have a mysql view in a Laravel project. I've written some reports against the view.
How do I seed the model in my unit tests? I have the model annotated as readonly, so I can't seed the data the normal way.
Here's my model:
namespace App\Models;
use App\Models\Model;
use MichaelAChrisco\ReadOnly\ReadOnlyTrait;
class FancyView extends Model
{
use ReadOnlyTrait;
protected $table = 'really_fancy_view';
}
I have a mysql view that gets created in a migration.
One idea we had was to seed the tables the view uses, then run create the view. But is this the right way to test against a mysql view? My view is created with raw SQL. Will Laravel be able to handle creating a view in a test environment?
I can't find anything online about testing against a view, let alone anything on SO.
What you can do, possibly, is create a folder in your tests with your gold set of data (be it sql insert files or csv's) and then just read in and run those against the database before running the tests (you may be able to leverage a migration as well, but I am personally not familiar with them in laravel).
You can leverage setUpBeforeClass in phpunit to accomplish this. Drop the test table if it exists before you read in the contents of your golden set and insert it into the database. Then use tearDownAfterClass to drop the table again after the tests have occurred.
This pattern can also be used for your proposed second solution, seeding the data into the source tables (only enough data to run the test efficiently) then create the view in the same step.
class DatabaseTest extends TestCase {
public function setUpBeforeClass() {
// make sure you're starting with a fresh state
$this->tearDownAfterClass();
// 1. seed database tables
// 2. run view generation query
}
public function tearDownAfterClass() {
// 1. drop view table
// 2. truncate seeded data
}
}
As for which is better, that really depends on what you're trying to test. Are you testing that the database can actually create the view? If so then it would make sense that you test that the view was able to be created at all. A benefit of this would be knowing that if the view somehow broke your tests would catch it right away.
Performance concerns shouldn't be an issue, because you should only be testing against enough data to verify that your view is being composed accurately.
Usually you do not need to unit test something that is fully tested in the library:
https://github.com/michaelachrisco/ReadOnlyTraitLaravel/blob/master/src/ReadOnlyTrait.php
If you were so inclined, you could make the table and populate it with faked data just as jardis suggested.
If you are looking to test views (or full on integration tests) within Laravel, you can use https://laravel.com/docs/5.6/dusk which looks to be a wrapper around Selenium.

Laravel randomly connects to different database

I have no idea how to google this since I mostly get tutorials about setting up multiple database connections.
At the moment, completely at random, my Laravel application gives me back an error about SQL, that the table could not be found. Laravel connects in this case to an entitely different database for some reason.
I connect in my .env and config/database.php to database1. Suddenly, an SQL error appears reading
Cannot find table `database2`.`table` in field list.
User also gets logged out when this error occurs.
Might be of use if i include that:
I sometimes use a custom user provider and sometimes the Laravel user provider. It depends on the subdomain. Error occurs on both configurations.
I dynamically add other database connections to the config, but never one to "database2". Database2 is a completely different application.
A search through the source code does not find any match with "database2"
Has anyone come across this problem? And if yes, how do I solve this? Thanks in advance!
Basically any Laravel Eloquent Model at seemingly Random will behave as if it was set with the public $connection="database 2"; variable.
Have you defined a model to use a different connection maybe?
Following the Laravel docs, this lets you use a different connection for specific models.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The connection name for the model.
*
* #var string
*/
protected $connection = 'connection-name';
}
One of the most common reason is because you are setting your Configuration in a dynamic way with variables, so the Cache gets cached with the wrong information in this case the default database connection.
Or you could be setting the configuration at runtime which also makes the Illuminate\Foundation\Console\ConfigCacheCommand.php to generate the wrong files.
Make sure your configurations are serializable. Try running the command.
php artisan cache:config

codeigniter project database upgrade scripts

I'm looking for ideas to properly handle my project's mysql table updates across environments. I've taken a look at the CI DB Forge class and I believe this might help me out a bit. My thoughts are to:
create a new file for each database install, table upgrade or change. The file would contain the raw mysql query to do each relevant task
run the upgrade scripts via hooks before any controllers are loaded
continue loading the project
Is this the correct thinking? This is pretty similar to how Magento handles database upgrades per extensions.
Sounds like you are looking for the Migrations class. This is a fairly new library, and the documentation at this moment is not too good in my opinion.
If you enable this library in application/config/migrations.php and load it, the it will create a database table called migrations. The workflow from there is the following:
Create a new file under application/migrations make sure you name it with sequentially numbered file name like 001_some_descriptive_name.php. The format is important, exactly 3 numbers and at least one _ after them.
In the new file create a class named after the file name, so the 001_some_descriptive_name.php should hold a class called Migration_Some_descriptive_name and extend the CI_Migration class. The class name casing is important, first Migration_ then one uppercase letter then lowercase.
Create a public up and a public down method inside the class
Inside the up method add your migration code, that changes the database. You can use the db forge library or just plain old $this->db->query() calls. Dbforge is more portable if you need to support multiple database systems its probably better to use that.
Inside the down method add the code that would reverse the effects of up. If up adds a column then down should drop that column, if up creates a table, down should drop that table and so on.
Once you finished your migration class bump the migration_version inside the migration config file.
Create a controller load the migration library and call $this->migration->current() this will check the version from the migration database table and run the migration classes up or down methods in order to reach the migration version in the config file you set at step 6. So for example if the database says you are at version 2, and the config says you should be on 5, then it will run the up method of the migrations with 003_..., 004_..., 005_.. filenames in order. If you set lower number then the current the down methods will be called. The database starts the counting from 0 so don't create a 000_... file.
If you feel adventurous you can create a hook that loads the migration class and runs the get_instance()->migration->latest() on every page load so every environment will auto update the db once you deploy a new migration class.

Automate database table creation from within CakePHP framework

I'm trying to write a webapp with CakePHP, and like most webapps I would like to create an installer that detects whether the database has been initialized and, if not, executes the installation process.
This process will be entirely automated (it assumes the database itself already exists, and that it is granted full administrative access through the anonymous account with no password...this is for a sandbox environment, so no worries about security), so it needs to be able to detect (regardless of the request!) if the database tables have been created and initialized, and if not, to perform that initialization transparently and then still serve up the user's original request.
I considered writing a sort of Bootstrap controller through which all requests are routed, and a single SQL query is run to determine if the database tables exist, but this seemed cumbersome (and the controller requires a corresponding model, which needn't be the case here). The other possibility is was to override AppModel and place within it the same test, but I was unsure how to do this, as there isn't any documentation along these lines.
Thanks in advance!
tl;dr version: What is the CakePHP equivalent (or how can the equivalent be written for CakePHP) of a J2EE servlet's "init()" method?
Check out the AppError class - may be there is a missing database table error that your can override and run your create database table SQL from there?
Something like this might do what you're looking for, but does require an extra query per model.
<?php
class AppModel extends Model {
var $create_query = false;
function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
if (!empty($this->create_query)) {
$this->query($this->create_query);
}
}
}
?>
Then you would just need to add var $create_query = 'CREATE TABLE IF NOT EXISTS ...;` in your models. The MySQL CREATE TABLE documentation has more information on IF NOT EXISTS.
However, I'm not certain that this isn't trying to call query() too early. Or before the check to see if the table already exists, which it would have to be. Otherwise, CakePHP would error out instead of creating your table. There isn't much documentation on the subject and your best bet for more information going to be to take a look at cake/libs/model/model.php directly.
Update: The code above would not work as written.
After looking into the Model class a little deeper, Model::__construct calls Model::setSource(). Model::setSource() checks to see if the table exists and throws an error if it doesn't. To use this method, that's where you'd have to override and insert the query. The code below may need to differ, depending on which version of CakePHP you're using.
<?php
function setSource($tableName) {
// From Model::setSource(). I believe this is needed to make query() work.
$this->setDataSource($this->useDbConfig);
// Create our table if we have a create query
if (!empty($this->create_query)) {
$this->query($this->create_query);
}
// Now call the parent
parent::setSource($tableName);
}
?>
Do you really want to make every request check that the database exists? This seems terribly wasteful.
The first time it checks it will set up the database, and then for every one of the next million requests it will check again even though the database has of course by this time been set up!
It's a better idea to require that whomever installs your application do some setup. You should provide a separate script that is not run during every request, but is run manually by the person during installation.
Re your comment about the best way to do this with CakePHP, have you looked at Cake's support for database schema migrations?
http://book.cakephp.org/view/734/Schema-management-and-migrations
I thought about doing this at one point in time, and came up with a good way to do it.
First off, a note: I believe your app needs write access to app/config/database.php, so you can write the correct information. I have a workaround, but let me explain the method by which the installer works.
PHP Scripts can rewrite themselves (or delete themselves) on the fly. Since the script is loaded into memory and THEN the bytecode is executed, it isn't dependent upon the file once it is done working. So you could theoretically have something that overrides the AppError class to run a specific controller (say your SetupWizardController) with all the regular, CakePHP stuff loaded. Once you go through the entire wizard, write the database.php file/setup anything else needed, overwrite the AppError and AppController files (I can see you needing the AppController to work differently if the app hasn't been installed in certain cases) with ones that are stored somewhere like in app/vendors/myapp. The script has finished executing and now your checks will not occur on subsequent runs.
This would get rid of the problem Bill Karwin brought up, utilize AppError, and make an installer.
The other way to do it is to include the idea of Environments in your application. Say you store your environments in app/config/environment.php. This file would be given write access to (and that access would be removed at the end of setup), or could be overwritten. Regardless, it would exist whenever someone attempts to deploy your application, and database.php automatically imports the databases from the environment.php file. The constructor of the database.php class would then overwrite it's $default database with the one specified in environment.php. The $default in database.php would reference a NoDB Datasource, which you can check in the AppController (I think you would be able to instantiate a dummy model and check it's datasource), and then push up the required Installer. Obviously you'd overwrite the AppController after this as well.

Categories