Create table with few rows with migration in laravel - php

I would like to create a table that will save the data for only 10 rows. How can I do it via migration in Laravel 5.3?

Laravel 5.3 provides seeding, also combined with model factories. I'm guessing you're using Eloquent models instead of query builder.
Model Factory
Here an example from Laravel (https://laravel.com/docs/5.3/seeding#using-model-factories)
factory(App\User::class, 10)->create();
This code creates 10 fake users via the User Eloquent model. The declaration of a fake user could be done in database/factories/ModelFactory.php.
Seeding
Again, a partial example from Laravel (https://laravel.com/docs/5.3/seeding#writing-seeders). You can call the model factory directly from the existing DatabaseSeeder (no need to create a new seeder).
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
factory(App\User::class, 10)->create();
}
}
Run
php artisan db:seed to seed the data in existing table structure
php artisan migrate:refresh --seed for completely rebuilding your database and running the seeders
Full documentation and examples, see the provided links above.

There is no such restriction. You can create table as usual and just add 10 rows. If you're asking about hwo to add 10 rows to the table, read about seeding.
Also, if you have just 10 simple rows, consider using config file for that. You can do something like this:
'my-data' => [
1 => ['name' => 'John', 'age' => 30],
2 => ['name' => 'Alan', 'age' => 40],
....
],
And access this data with config('my-config.my-data')

If you want a dummy data to be filled into your table without any hassle you can take a use of Faker Package by fzaninotto.
The process would be like
Install faker via running below command in terminal (from project's root directory):
composer require fzaninotto/faker
Then in your routes.php you can push dummy entries like this:
// Just for an example I am using route to demonstrate the general use
Route::get('/customers',function(){
$faker = Faker\Factory::create();
$limit = 10; // this value sets number of rows to be created
// generate data by accessing properties
for ($i = 0; $i < $limit; $i++) {
User::create([
'name' => $faker->name,
'email' => $faker->email,
'phoneNumber' => $faker->phoneNumber,
]);
}
});
This would create total 10 rows in your table with some dummy data.. Hope this helps

Related

CakePHP Unit tests are ignoring Fixtures/test databases and using application's data/databases

I have a legacy application which is running on CakePHP 4.3.8. Historically this application never had any unit tests but we're starting to add them as we work on new features.
The application uses 8 different MariaDB database connections. These are configured in config/app_local.php and all have their own key. There is a corresponding "test" database for each one prefixed with test_. As an example:
dev_notification_db: Local database for a system notifications feature of the app.
test_notification_db: Test database for the above
All database names follow this convention and have appropriate credentials and connection details to a local install of MariaDB 10.x. A schema with the appropriate name has been created locally and access has been granted to the user/pass referenced in the configuration. We have not had any connection or permissions errors.
In the dev_notification_db there are around 3400 rows of data.
I'm trying to write some unit tests following CakePHP's Testing docs. In this case I want test_notification_db to contain just 3 rows of data since for a particular test I'm writing I don't need all 3400 rows from the application database and certainly don't want them in a version controlled Fixture.
I have created a Fixture in tests/Fixture/NotificationsFixture.php which contains the 3 rows I want to add to the test database, test_notification_db. This file was created using the following command:
bin/cake bake fixture --connection dev_notification_db --conditions 1=1 --count 3400 --records Notifications
When the file was created it contained all 3400 rows of data from dev_notification_db. I manually removed all but the 3 of them that were necessary for testing purposes. The reason I did it in this way is because using the command above also reads the table structure and adds it to NotificationsFixture.php, meaning the table with the same columns/data types can be created on test_notification_db.
NotificationsFixture.php looks like this
class NotificationsFixture extends TestFixture
{
public $fields = [
// This is essentially the schema of the `notifications` table
// e.g. 'id' => ['type' => 'integer', 'length' => null, 'unsigned' => true, 'null' => false, 'default' => null, 'comment' => '', 'autoIncrement' => true, 'precision' => null]
// Other columns...
];
public function init(): void
{
$this->records = [
[
'id' => 1,
'column1' => 'foo',
'column2' => 'bar',
'column3' => 'baz',
],
// Other rows of test data...
}
}
In the init() method above, $this->records contains 3 rows of data which are the ones I want to use in my fixture.
According to the Creating Fixtures section of the CakePHP docs, it says
Fixtures defines the records that will be inserted into the test database at the beginning of each test.
Therefore my understanding is that the 3 rows in $this->records should end up in the test_notification_db when I'm running my tests.
There isn't anything else in the NotificationsFixture.php file, for example something telling it to use a different Data Source from the app_local.php config file: it has $fields and the init() method, and that's all.
My test for this is in tests/TestCase/Model/Table/NotificationsTest.php. One of my tests relies on it reading the Fixture data, i.e. the 3 rows from test_notification_db. In order to do this I've added the following to my test:
protected $fixtures = [
'app.Notifications',
// ...
];
public function setUp(): void
{
parent::setUp();
$this->Notifications = TableRegistry::getTableLocator()->get('Notifications');
}
public function testGetNotifications()
{
$userNotifications = $this->Notifications->find()->where(['user_id' => 1])->count();
debug($userNotifications);
die;
}
To explain my understanding of the code above:
The $fixtures array contains app.Notifications. This is consistent to what's shown in the CakePHP docs regarding Loading Fixtures in your Test Cases. It specficially says:
After you’ve created your fixtures, you’ll want to use them in your test cases. In each test case you should load the fixtures you will need. You should load a fixture for every model that will have a query run against it.
So my understanding is I'm loading the NotificationsFixture.php file referenced earlier.
The setUp() method gives me a reference to the Model that interacts with the notifications table, i.e. src/Model/Table/NotificationsTable.php. Given that this is being run in a Test Suite - and after reviewing the docs - this should be interacting with the test database (test_notification_db) NOT the application database (dev_notification_db).
The actual test uses the model in the point above and attempts to find notifications for a given user ID. The count in this case comes back as 400. In the application database there are 400 rows for the user ID given. However, in the fixture there are only 3 rows (all of which are for the user in question). Therefore this should come back as 3, not 400.
Upon debugging the full result set it's clear that the data is being loaded from dev_notification_db and not the test database, test_notification_db.
Why is this? If the purpose of the test suite is to be able to interact with the test database and the Fixtures are - quote - "records that will be inserted into the test database at the beginning of each test" then this is certainly not doing that.
What is missing here, or making it interact with the application's database and not the separate database for testing purposes?

CakePHP Migration Script doesn't update the Table Model

I need to add an admin column to my user table in my database. I created the migration script with the following command.
bin/cake bake migration AddAdminToUsers admin:boolean
This mostly did what I wanted, I just changed the default value to false. My Migration script now looks like this.
<?php
use Migrations\AbstractMigration;
class AddAdminToUsers extends AbstractMigration
{
public function change()
{
$table = $this->table('users');
$table->addColumn('admin', 'boolean', [
'default' => false,
'null' => false
]);
$table->update();
}
}
Also, oddly enough, I've tried this several times and each time I'm only able to run this migration script once. I have to delete it and re-bake a new one if I want another one to work.
When you run a migration it marks as migrated and you can not run it one more time unless do the rollback. Rollback will cancel previous migration and you will be able to run it one more time.Here is fully docs for plugin that cakphp using for migrations.

Laravel seeding with factory but extending it

I want a database with two tables Users and Companies and the users table has a foreign key with the company id. So 1 company can have multiple users.
I created the models for this in laravel and also created a factory for each of the tables.
In my seeders I want to create multiple data lines at once and found this solution:
factory(App\Company::class, 10)->create();
This works fine for the company table. But I need to extend this for the users table so it searches for available company Ids.
Any ideas how I can do this?
If I can't search for available Ids, I would also be happy to extend it with the "company_id" field and give it random value from 1-10 (beacause I know that these are the Ids for now).
So basically I want to extend this to use the fields from the factory, but extend it with another field "company_id":
factory(App\Users::class, 10)->create();
Here is the User factory code:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'postcode' => $faker->postcode,
'city' => $faker->city
];
});
When dealing with such relationships, defining model factories can be a pain. You have a few options though.
Random blind IDs
If you're sure that a common range of IDs is available, you can just generate a random ID to make the relationship work:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => rand(1, 10),
];
});
Inline factories
This was the most widely used approach as promoted by Laracast's screencasts:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => factory(App\Company::class)->create()->id,
];
});
Or you can go the other way around and create one or more users in the company factory. This way, you only need to run factory() on one of your entities.
Random ID from database
As of Laravel's 5.2 release, you have access to an Eloquent method called inRandomOrder:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
// ...
'company_id' => Company::inRandomOrder()->first()->id,
];
});
Alternatives for Laravel releases before 5.2:
// Laravel 4.2.7 - 5.1:
User::orderByRaw("RAND()")->get();
// Laravel 4.0 - 4.2.6:
User::orderBy(DB::raw('RAND()'))->get();
// Laravel 3:
User::order_by(DB::raw('RAND()'))->get();
// Source: http://stackoverflow.com/a/13931676/65732
It looks like that, you want to create Companies and Users and also you want to attach users with companies at the same time. If this is what you are trying to achieve then you can do it easily using something like this:
factory(App\Company::class, 10)->create()->each(function($factory) {
$factory->users()->save(factory(App\User::class)->make());
});
In this case, you have to define the users relationship method in your Company model using the right foreign key.
This will create 10 companies and return a collection so on that collection using each method a single user will be created and attached with that company. Also, check the documentation.
If you're using Laravel 5.2 or above you can use this:
'company_id' => Company::inRandomOrder()->first()->id

Adding fixtures with its associations in Cakephp 3.x

after I did some research, I didn't find any proper answer to this question.
I'm starting writing tests for my CakePHP (3.x) app and I was wondering how can we add fixtures and their associations?. In other words, how can we link a fixture to another without writing the Primary and foreign keys directly inside the fixture.
i.e
I have a Users table and a UserProfiles table.
User hasOne UserProfile through user_id column. Here's my user fixture.
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UsersFixture extends TestFixture
{
public $import = ['table' => 'users'];
public $records = [
[
'username' => 'mark#mail.com',
'primary_role' => 1
'is_active' => 1,
]
];
}
And here's my UserProfile fixture
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UserProfilesFixture extends TestFixture
{
public $import = ['table' => 'user_profiles'];
public $records = [
[
'first_name' => 'Mark',
'last_name' => 'Yellowknife',
]
];
}
How do I link both record together? Aka, how can I tell the user profile record to add its user_id to be linked to the user record?
Must be a way to do this with Fixtures without writing the keys manually.
Thanks guys!
You have to define this in the fixtures. The only other way would be to issue update queries at runtime to set the foreign keys, but that's anything but a good idea.
Other than that there isn't really a way to do this, I mean, how would any code know which records needs to be associated with each other without you explicitly defining it?
So, you'll have to at least define the foreign key, and depending on how primary keys are being generated (auto incrementing integers, UUIDs, etc), you might have to define them too.

Laravel DB Seeds - Test Data v Sample Data

I'm probably misunderstanding exactly how this works, but what's the best way to accomplish this? I have something in mind but it seems quite hacky.
I have a set of sample data which I use to test my application. This is seeded via the built in seeder in Laravel. This contains things like example users, addresses, documents etc.
I also have a set of default data which should go in production. I currently add this directly in the migration. For example, if I was adding a table for account_roles, I might include the following at the bottom of the migration
$account_admin = array('role' => 'Account Administrator', 'flag' => 'ACCOUNT_ADMIN');
$account_owner = array('role' => 'Account Administrator', 'flag' => 'ACCOUNT_OWNER');
DB::table('account_roles')->insert($account_admin);
DB::table('account_roles')->insert($account_owner);
This way, on production, I just migrate the database to insert any production ready database values, and on staging/development, I can refresh the migrations and then seed the database with sample data.
Is there any other (better) way to do this?
You could run a check on the current environment in your seeder file, and seed as needed
<?php
class DatabaseSeeder extends Seeder {
public function run()
{
Eloquent::unguard();
if (App::environment() === 'production')
{
$this->call('ProductionSeeder');
}
else
{
$this->call('StagingSeeder');
}
}
}

Categories