I have a table
public function up()
{
Schema::create('parties', function (Blueprint $table) {
$table->id();
$table->integer('place_id');
$table->tinyInteger('status')->default(0);
$table->dateTime('utc_date');
$table->dateTime('local_date');
$table->timestamps();
});
Schema::table('parties', function (Blueprint $table) {
$table->index('place_id');
$table->foreign('place_id')
->references('id')
->on('places')
->onDelete('restrict');
});
}
and model factory
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\Party;
use Faker\Generator as Faker;
$factory->define(Party::class, function (Faker $faker) {
$date = $faker->dateTimeBetween(now()->subDays(3), now());
return [
'place_id' => $faker->numberBetween(1, 9),
'status' => $faker->numberBetween(0, 3),
'utc_date' => $date,
'local_date' => \Carbon\Carbon::make($date)->addHours(3),
];
});
If i call the method create in HomeController it works correctly
factory(Party::class, 10)->create();
but when i call it in my tests I got an error
SQLSTATE[42703]: Undefined column: 7 ERROR: column "utc_date" of
relation "parties" does not exist LINE 1: insert into "parties"
("place_id", "status", "utc_date", "lo...
^ (SQL: insert into "parties" ("place_id", "status", "utc_date", "local_date",
"updated_at", "created_at") values (5, 3, 2020-08-04 00:34:52,
2020-08-04 03:34:52, 2020-08-04 11:42:18, 2020-08-04 11:42:18)
returning "id")
<?php
namespace Tests\Feature\API;
use App\User;
use Tests\TestCase;
use Laravel\Sanctum\Sanctum;
use App\Models\{Party, Place};
use Illuminate\Foundation\Testing\RefreshDatabase;
class PartiesControllerTest extends TestCase
{
use RefreshDatabase;
private User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
public function test_get_parties()
{
factory(Party::class)->create([
'place_id' => factory(Place::class)->create()->id,
]);
dd(Party::all());
Sanctum::actingAs($this->user);
}
}
I am using second database for testing. I connected to this db and found that this table has not been refreshed. I don't know why because I used RefreshDatabase trait. I added this field manually for solving this problem.
Try to run php artisan migrate:fresh. If you use another database for testing and using another .env file then try to run php artisan migrate:fresh --env=testing.
For example: you can use file env.testing and APP_ENV=testing inside this file.
Maybe you added this field later and did not refresh DB. Also, check if the field exists in visual DB.
If it didn't help try to run composer dump-autoload and retry.
please make sure the fillable array in model has this key,
and try using carbon instead of faker with date
(Carbon::now())->subDays(3);
Related
I am having an issue inserting a record into the database. I am a beginner with the Yii framework, so I may have made some stupid mistakes.
This is from the SiteController
public function actionCreatePost(){
$model = new PostForm();
$post = new Post();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
$post->body = $model->body;
$post->title = $model->title;
$post->save();
return $this->redirect('index');
}else {
return $this->render('createPost', ['model' => $model]);
}
}
This is from the Post class
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => new Expression('NOW()'),
],
[
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'id_author',
]
];
}
The issue is that you have created a PostForm class for the form (which is correct) but you are then trying to load the response into the Post class - a completely different class. This won’t work without modification.
If you have a look at the response:
var_dump(Yii:$app->request->post());
You will see the form data is located within the PostForm key. Yii will therefore only load the data into the PostForm class.
The correct solution is therefore to create a savePost() function within the PostForm eg:
public function savePost(){
$model = new Post();
$model->propertyABC = $this->propertyABC
...etc...
$model->save();
So the action would appear as follows:
$model = new PostForm();
If($model->load(Yii::$app->request->post()) && $model->validate()){
$model->savePost();
The other option is to rename the key from PostForm to Post. Yii will then load the data but this is not the best approach as it is a bit obscure.
Hope that helps
I would guess the issue is with the validation.
I can see several issues I will point out. First, I cannot figure out why are you creating a new PostForm, loading the data in it and verifying it, just to dump some values in a new Post and save it. Are there some functions, you are running in the PostForm model, that are triggered by load or verify? If that is not the case, I would suggest dropping one of the models, and using only the other. Usually, that is the Form model. It serves as a link between the ActiveForm and the model handling everything. You can do everything in the createPost() function in the Form model, and then in the controller it will look like
if ($model->load(Yii::$app->request->post())) {
$model->save();
return $this->redirect('index');
}
Second of all, you can dump post->getErrors() before the save to see if there are any errors with the validation. What you can also do, is call $post->save(false) instead. If you pass false to it, it will not trigger $post->validate(), and some errors can be neglected. Please, let me know if there is anything unclear.
I'm very new to Laravel and Database and I'm trying to understand how to insert data into my database. Please be patient the question can sounds dummy for you.
STEP
I created a table in migrations. Example of a table:
public function up(){
Schema::create('job-urls', function (Blueprint $table) {
$table->increments('id');
$table->foreign('job_id')->references('id')->on('jobs');
$table->string('url')->index();
$table->string('hash');
$table->timestamp('created_at')->nullable();
$table->timestamp('updated_at')->nullable();
STEP
I have two csv file that correspond to the field url and hash and I want to insert them. I created a new file in migration called populate_jobs_url
class PopulateJoburls extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up(){
$fileurls = fopen('../data/urls.csv', 'r');
$filehash = fopen('../data/urls_hash.csv', 'r');
while (($row = fgetcsv($fileurls, 0, ',')) !=FALSE){
DB::table('joburls')->insert(
array(
'url' => $row,
)
);
}
while (($row = fgetcsv($filehash, 0, ',')) !=FALSE){
DB::table('joburls')->insert(
array(
'hash' => $row,
)
);
}
}
Can you help me to understand how I check if the table is correctly filled? Is this approach correct? How could I insert data otherwise in my Database? Unfortunately all examples on the web deal with inserting manually data with a form.
Thanks
Seeding the table inside of a migration file is not the best practise. You can take advantage of Seeders, which is right way to fill your table with test or actual data.
First, create a seeder file with php artisan make:seeder PopulateJobUrls command. Then you can arrange your seeder like this:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PopulateJobUrls extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$fileurls = fopen('../data/urls.csv', 'r');
$filehash = fopen('../data/urls_hash.csv', 'r');
// Rest of your seeding logic...
}
}
You should reference your seeder from database/seeds/DatabaseSeeder.php in the run method:
$this->call(PopulateJobUrls::class);
Run php artisan db:seed or if you want to be more specific, php artisan db:seed --class=PopulateJobUrls and you are good to go with your correctly filled data!
I have a few tables in a database that need specific data. I know I can always save the SQL command and execute them but I wonder if Laravel has some sort of specific command.
You can use seeders for test data, like #Alexey says. If you need the data to persist in all environments (e.g. local and production), you can insert the data after creating the table, with the Query Builder.
e.g.
<?php
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
DB::table('posts')->insert([
'title' => 'Hello, world!',
'body' => 'This post will be created after migrating.',
]);
}
Laravel uses seeder classes to fill tables with data after migrations:
You need to create and register seeders and then run this command to run migration and seed the data:
php artisan migrate --seed
I'm wondering how one can seed in Yii a table once it is created with migration?
I've got a migration with an up-method:
public function up()
{
$this->createTable('users',array('id'=>"pk",
'login'=>'string NOT NULL'));
echo "table 'users' is created.\n";
return true;
}
I've got as well corresponding Users model and its CRUD actions. When I try to execute another migration with an up-method
public function up()
{
$user = new Users;
$user->login = "Bob";
return $user->save();
}
I get the following error:
PHP Error[2]: include(users.php): failed to open stream: No such file or directory
in file MyYiiRoot\yii\framework\YiiBase.php at line 421
I've managed to achieve the desired result by using query builder (by means of insert command), but I hope there is a nicer way out.
Use
public function safeUp()
{
$this->insert('users',array(
'login'=>'Bob'));
}
You can also do update, delete and a host of other actions. Look at http://www.yiiframework.com/doc/api/1.1/CDbMigration for more information
I have a console command to do a consumer time, AND I need to know how to call (execute) it in a web application action in YII.
class MyCommand extends CConsoleCommand{
public function actionIndex(){
$model = new Product();
$model->title = 'my product';
...
$model->save();
.
.
.
}
}
I want to execute this code.
try this:
Yii::import('application.commands.*');
$command = new MyCommand("test", "test");
$command->run(null);
The 2 parameters with value "test" must be set but do not have an impact, they are used for the --help option when using the console.
/**
* Constructor.
* #param string $name name of the command
* #param CConsoleCommandRunner $runner the command runner
*/
public function __construct($name,$runner)
{
$this->_name=$name;
$this->_runner=$runner;
$this->attachBehaviors($this->behaviors());
}
https://github.com/yiisoft/yii/blob/master/framework/console/CConsoleCommand.php#L65
Try this
Yii::import('application.commands.*');
$command = new GearmanCommand('start', Yii::app()->commandRunner);
$command->run(array('start', '--daemonize', '--initd'));
where array('start', '--daemonize', '--initd') is a action and action parameters
I had same problem - i need to call action from inside controller and from command
I said same problem because it actually same - you have action which you need to call from console, and call it from controller too.
If you need to call an action(command) as a part of controller action, then i think you need to modify this solution a little. Or is my solution is enough for you?
So here is my solution:
first create action as said in http://www.yiichina.net/doc/guide/1.1/en/basics.controller#action
class NotifyUnsharedItemsAction extends CAction
{
public function run()
{
echo "ok";
}
}
then in controller action is loaded as usuall:
class TestController extends Controller
{
public function actions() {
return array(
'notifyUnsharedItems'=>'application.controllers.actions.NotifyUnsharedItemsAction',
);
}
and in command i run action in such way:
class NotifyUnsharedItemsCommand extends CConsoleCommand
{
public function run($args)
{
$action = Yii::createComponent('application.controllers.actions.NotifyUnsharedItemsAction',$this,'notify');
$action->run();
}
}
Accepting that we are on linux server, for Yii 1.1 real life example would be:
$run = '/usr/bin/php ' . Yii::getPathOfAlias('root').'/yiic' [command]
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $run, '/dev/null', '/dev/null'));
This will run Yii console command in the background.
Yii is PHP -> you can use the standard php constructs specified at http://php.net/manual/en/function.exec.php and the related methods near the bottom of the page, depending on what exactly you want to achieve.
Also, another very clean solution from cebe on gist:
<?php
// ...
$runner=new CConsoleCommandRunner();
$runner->commands=array(
'commandName' => array(
'class' => 'application.commands.myCommand',
),
);
ob_start();
$runner->run(array(
'yiic',
'idbrights',
));
echo nl2br(htmlentities(ob_get_clean(), null, Yii::app()->charset));
Yii::app()->end();
Typically what you should do in these situations is refactor.
Move the "common" code out of the MyCommand and place it into a class located in the components folder.
Now you can place any head on top of the "common" code without altering your functionality. For example:
protected/components/Mywork.php:
<?php
class Mywork
{
public function doWork()
{
$model = new Product();
$model->title = 'my product';
...
$model->save();
...
}
}
protected/controller/MyworkController.php:
<?php
class MyworkController
{
public function actionDowork()
{
$mywork = new Mywork;
...
}
}
protected/commands/MyworkCommand.php:
<?php
class MyworkCommand extends CConsoleCommand
{
public function run($args)
{
$mywork = new Mywork;
...
}
}
This approach makes testing easier too as you can test Mywork as a single unit outside of the view you are using.