I am trying to insert hard coded data with QueryBuilder insertGetId() method.
my code is-
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class StudentController extends Controller
{
public function addStudent()
{
$foreign_key = DB::table('students')->insertGetId([
'id' => 'stu-000002',
'name' => 'Ahsan',
'email' => 'ahsan#example.net',
]);
echo $foreign_key;
}
}
My migration file is-
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->string('id', 30)->primary();
$table->string('name', 100);
$table->string('email', 100)->unique();
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('students');
}
};
My route is -
Route::post('/add-student', [StudentController::class, 'addStudent']);
But result is -
Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: POST.
But when I try to insert data with get method like-
Route::get('/add-student', [StudentController::class, 'addStudent']);
Data has been inserted . But primary key has been retrived 0 as primary key is custom string.
How can I solve this problem. Thank you.
run this command in terminal:
php artisan route:cache
So what this command does is, it registers new routes or gives an error if there is something wrong with your route file.
There are two problems with what you're doing:
Problem 1:
The first is the MethodNotAllowedException. I guess you're trying to use a GET request on a POST URL. This won't work, because Laravel blocks the 'wrong' method.
Use POST when you have data to submit (or if you really want to stick to the 'use post when saving'-creed use a form). Use GET when you want to access an URL.
Problem 2
According to the API (This one) insertGetId returns an integer which is the ID. Since your ID's are strings, you can't use that method.
A solution to that problem would be to change the code like this:
public function addStudent()
{
$student_id = 'stu-000002'
$insert = DB::table('students')->insert([
'id' => $student_id,
'name' => 'Ahsan',
'email' => 'ahsan#example.net',
]);
if ( ! $insert ) {
return false;
}
echo $student_id;
}
The insert method returns a boolean. Leveraging that you can check whether or not the entry was inserted. If it is, your ID should be good.
Related
I've made CRON Job using Laravel's task scheduling. But what I need is to store somewhere when that task was last ran,
Does anyone have any methods of how they store that and also, If Laravel outputs anything that can tell you when it was last ran?
Thanks
Not possible directly, however it is possible if you cache a date-time string on each run (either at the beginning or end of your script).
Cache::rememberForever('name_of_artisan_task', function () {
return now()->toDateTimeString();
});
The example shows using the Cache facade's ::rememberForever method to create a key/value of the last time the task was ran. As the name suggests, this is saved forever.
You can easily retrieve this date and time using the cache() helper:
cache('name_of_artisan_task');
The con with this method is that if your cache is cleared, you will not longer have this stored.
Using a cache is not a safe way to do this, as #thisiskelvin hinted, clearing the cache will remove the data (which should happen on each deployment) but he didn't provide an alternative
So here is one if you need this date reliably (if you use it to know the interval to run an export for instance)
In which case I recommend creating a model php artisan make:model ScheduleRuns -m
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* #property string $task
*/
class ScheduleRuns extends Model
{
public const UPDATED_AT = false;
public $timestamps = true;
protected $attributes = [
'task' => '',
];
protected $fillable = [
'task',
'created_at',
];
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('schedule_runs', function (Blueprint $table) {
$table->id();
$table->string('task');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('schedule_runs');
}
};
Then use schedule hooks to create it (or do it within the task if you want to avoid possible seconds differences)
$schedule->command('export:users')
->weekly()->onSuccess(fn () => ScheduleRuns::create(['task' => 'export:users']))
And to retrieve the latest run
ScheduleRuns::query()->where('task', 'export:users')->latest();
Just write log each time the task was run, or you can push it into database.
<?php
namespace App\Console\Commands\Tasks;
use Illuminate\Console\Command;
class ScheduledTask extends Command
{
public function handle()
{
//
// ...handle you task
//
$file = 'logs/jobs/' . __CLASS__ . '.log';
$message = 'Executed at: ' . date('Y-m-d H:i:s', time());
file_put_contents(storage_path($file), $message, FILE_APPEND);
}
}
i want to access to data in the function of the controller using relationships on Laravel.
I will explain first my code:
I have 3 tables, projects, client and client_project.
At this moment, client_project don't have any relationship, i just add it manually.
Now i want to use relationships on laravel, but it's a bit confusing (for me at least).
I think it's not too much important the code of projects and clients table, just have id like primary key, and some fields more.
My migration of client_project looks like here:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateClientProjectTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('client_project', function(Blueprint $table)
{
$table->increments('id');
$table->integer('client_id');
$table->integer('project_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('client_project');
}
}
Client_Project model looks like here:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Client_Project extends Model
{
protected $fillable = ['client_id','project_id'];
public $table = 'client_project';
public function clients()
{
return $this->hasMany('App\Models\Project');
}
public function projects()
{
return $this->hasOne('App\Models\Client');
}
}
One client can have more than one project, but one project is only created by one client. I think relationships are declared good.
(At first, i think with relationships i don't need to make the client_project table), but i think that's a wrong idea. I want to make it with this table too.
So, now, the problem it's when i try to call on the function controller, i think i can access to data using por example:
App\Models\Project::find(1), like doc of laravel says.
The function is this:
$client = new Client();
$client->name = $request->input("nameClient");
$client->slug = $request->input("slugClient");
$client->priority = $request->input("priorityClient");
$client->save();
$client_project = new Client_Project();
$client_project->client_id = App\Models\Client::max('id');
$client_project->project_id = App\Models\Projects::max('id');
$client_project->save();
The part of the client, is working. I just take the value of some inputs and i create a new one.
The problem is with $client_project. I want to make it dynamic. I create the client and the project, and my code get the last one(the bigger id), and the last one(the bigger id too) of projects.
How can i access them using relationships?
Maybe need edit migration of client_project and put some key in project_id or client_id?
If need more information, please ask it.
Any help will be appreciated!
Here is your ans. You are going good way you created pivot table for client and project so you can attached as many projects to any client. Here is relationship with model.
Client Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Client extends Model
{
public function projects() {
return $this->belongsToMany(Project::class,'client_project');
}
}
Project model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Projects extends Model
{
public function client() {
return $this->belongsToMany(Client::class,'client_project');
}
}
?>
For Save project id use following way in controller method
$client = new Client();
$client->name = $request->input("nameClient");
$client->slug = $request->input("slugClient");
$client->priority = $request->input("priorityClient");
$client->save();
$project = new Project();
//include fields as per your table
$project->save();
$client->projects()->attach($project->id);
.
I'm very confused, i try to find what's wrong but i don't find it..
My migration file:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateClientProjectTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('client_project', function(Blueprint $table)
{
$table->increments('id');
$table->integer('client_id');
$table->integer('project_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('client_project');
}
}
First of all, i check table is created, and it is.
Then, the route who calls to the controller is this: (is prefixed by admin)
Route::post('projectsclients/postUpload', ['uses' => 'AdminController#storeProjectsClients', 'as' => 'admin.projectsclients.store']);
The functions looks like here:
$client_project = new Client_Project();
$client_project->client_id = DB::table('clients')->orderby('id','DESC')->take(1)->get();
$client_project->project_id = DB::table('projects')->orderby('id','DESC')->take(1)->get();
$client_project->save();
And the error:
Base table or view not found: 1146 Table 'web.client__projects'
doesn't exist
The problem is my table is client_project not client__projects.
Where i have to fix this?
Thanks a lot, any help will be appreciated.
You shouldn't be breaking up class name with _ for one. It should be named ClientProject. Generally if there is a problem with the table you would edit the modal and add a
ClientProject.php
public $table = 'client_project';
You are not following Laravel naming conventions. To solve this particular issue yo can explicitly define tabe name. In class Client_Projects definition add this:
protected $table = 'client_project';
But to learn about naming conventions I suggest reading related section in documents here https://laravel.com/docs/5.4/eloquent#eloquent-model-conventions
I'm trying to add a relation to a factory model to do some database seeding as follows - note I'm trying to add 2 posts to each user
public function run()
{
factory(App\User::class, 50)->create()->each(function($u) {
$u->posts()->save(factory(App\Post::class, 2)->make());
});
}
But its throwing the following error
Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::s
ave() must be an instance of Illuminate\Database\Eloquent\Model, instance
of Illuminate\Database\Eloquent\Collection given
I think its something to do with saving a collection. If re-write the code by calling each factory model for the post separately it seems to work. Obviously this isn't very elegant because if I want to persist 10 or post to each user then I'm having to decalare 10 or lines unless I use some kind of for loop.
public function run()
{
factory(App\User::class, 50)->create()->each(function($u) {
$u->posts()->save(factory(App\Post::class)->make());
$u->posts()->save(factory(App\Post::class)->make());
});
}
* UPDATED *
Is there any way to nest the model factory a 3rd level deep?
public function run()
{
factory(App\User::class, 50)
->create()
->each(function($u) {
$u->posts()->saveMany(factory(App\Post::class, 2)
->make()
->each(function($p){
$p->comments()->save(factory(App\Comment::class)->make());
}));
});
}
Since Laravel 5.6 there is a callback functions afterCreating & afterMaking allowing you to add relations directly after creation/make:
$factory->afterCreating(App\User::class, function ($user, $faker) {
$user->saveMany(factory(App\Post::class, 10)->make());
});
$factory->afterMaking(App\Post::class, function ($post, $faker) {
$post->save(factory(App\Comment::class)->make());
});
Now
factory(App\User::class, 50)->create()
will give you 50 users with each having 10 posts and each post has one comment.
Try this. It worked for me:
factory(\App\Models\Auth\User::class, 300)->create()->each(function ($s) {
$s->spots()->saveMany(factory(\App\Models\Spots\Parking::class, 2)->create()->each(function ($u) {
$u->pricing()->save(factory(\App\Models\Payment\Pricing::class)->make());
}));
$s->info()->save(factory(\App\Models\User\Data::class)->make());
});
For a 3rd level nested relationship, if you want to create the data with the proper corresponding foreign keys, you can loop through all the results from creating the posts, like so:
factory(App\User::class, 50)->create()->each(function($u) {
$u->posts()
->saveMany( factory(App\Post::class, 2)->make() )
->each(function($p){
$p->comments()->save(factory(App\Comment::class)->make());
});
});
To answer the initial problem / error message:
The problem indeed has to do with saving the data. Your code:
$u->posts()->save(factory(App\Post::class, 2)->make());
... should be changed to
$u->posts()->saveMany(factory(App\Post::class, 2)->make());
From the laravel docs:
You may use the createMany method to create multiple related models:
$user->posts()->createMany(
factory(App\Post::class, 3)->make()->toArray()
);
That means:
when only creating one model with the factory, you should use save() or create()
when creating multiple models with the factory, you should use saveMany() or createMany()
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', //
password
'remember_token' => Str::random(10),
];
});
$factory->define(Post::class, function ($faker) use ($factory) {
return [
'title' => $faker->sentence(3),
'content' => $faker->paragraph(5),
'user_id' => User::pluck('id')[$faker->numberBetween(1,User::count()-1)]
];
});
Let me, explain How to add multi level of relationship of call factory in Laravel 9 By concept of new school and old school.
The new school is:
\App\Models\Author::factory()
->has(
\App\Models\Article::factory(1)
->has(
\App\Models\Comment::factory(9)
->has(
\App\Models\Reply::factory(2)
)
))->create();`enter code here`
That's for Laravel 9 . There's anthor way call Magic method. let me explain that:
\App\Models\Author::factory()->hasArticles(1)->hasComments(9)->hasReplies(2)->create();
this hasArticles() is the name of method of relationship in parent model should convert the name with has. for example: comments() convert to hasComments().
Now lets explain old school that's still prefect in some cases and still works good with Laravel 9.
\App\Models\Author::factory(1)->create()
->each(function($a) {
$a->articles()->saveMany( \App\Models\Article::factory(2)->create() )
->each(function($p){
$p->comments()->saveMany(\App\Models\Comment::factory(5))->create()
->each(function($r){
$r->replies()->saveMany(\App\Models\Reply::factory(5))->create();
});
});
});
of course you can replace method saveMany() by save() as your relationship you have.
also you can replace method create() by make() if you want to doesn't save in database for test purposes.
Enjoy.
In version laravel 9, use like this
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* #extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* #return array<string, mixed>
*/
public function definition()
{
return [
'user_id' => User::factory(),
'title' => fake()->paragraph()
];
}
}```
My application needs a pre registered data set to work. So i need to insert them in the database when i set up the application.
Laravel propose two mechanisms :
Database migrations : "They allow a team to modify the database schema and stay up to date on the current schema state."
Database seeding : "Laravel also includes a simple way to seed your database with test data using seed classes."
When I read this description, none of these solutions seems to be adapted.
A similar question has been asked on stackoverflow and answered. The answer proposes to use the a database seeder to populate the database by detecting the current environment :
<?php
class DatabaseSeeder extends Seeder {
public function run()
{
Eloquent::unguard();
if (App::environment() === 'production')
{
$this->call('ProductionSeeder');
}
else
{
$this->call('StagingSeeder');
}
}
}
Of course, this solution works. But i am not sure that it is the right way to do this, because by inserting data using seeders you are losing all the advantages provided by the migration mechanism (database upgrate, rollback...)
I want to know what is the best practice in this case.
Laravel development is about freedom. So, if you need to seed your production database and think DatabaseSeeder is the best place to do so, why not?
Okay, seeder is mainly to be used with test data, but you'll see some folks using it as you are.
I see this important kind of seed as part of my migration, since this is something that cannot be out of my database tables and artisan migrate is ran everytime I deploy a new version of my application, so I just do
php artisan migrate:make seed_models_table
And create my seedind stuff in it:
public function up()
{
$models = array(
array('name' => '...'),
);
DB::table('models')->insert($models);
}
I've often found myself wondering what the right answer to this is. Personally, I'd steer clear of using seeding to populate required rows in the database as you'll have to put a load of conditional logic in to ensure that you don't attempt to populate something that's already there. (Deleting and recreating the data is very inadvisable as you could end up with key mismatches and if you're using cascading deletes you may accidentally wipe a load of your database by mistake! ;-)
I put the 'seeding' of rows into the migration script as the chances are, the data will need to be there as part of the rollout process.
It's worth noting that you should use the DB class instead of Eloquent models to populate this data as your class structure could change over time which will then prevent you from re-creating the database from scratch (without rewriting history and changing you migration files, which I'm sure is a bad thing.)
I'd tend to go with something like this:
public function up()
{
DB::beginTransaction();
Schema::create(
'town',
function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
}
);
DB::table('town')
->insert(
array(
array('London'),
array('Paris'),
array('New York')
)
);
Schema::create(
'location',
function (Blueprint $table) {
$table->increments('id');
$table->integer('town_id')->unsigned()->index();
$table->float('lat');
$table->float('long');
$table->timestamps();
$table->foreign('town_id')->references('id')->on('town')->onDelete('cascade');
}
);
DB::commit();
}
This then allows me to 'seed' the town table easily when I first create it, and won't interfere with any additions made to it at run-time.
This is what I use in production.
Since I run migration on each deployment
artisan migrate
I create a seeder (just to keep seeding data out of migration for easy access later) and then run that seeder along with the migration
class YourTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
//migrate your table // Example
Schema::create('test_table', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->softDeletes();
});
//seed this table
$seeder = new YourTableSeeder();
$seeder->run();
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::drop('test_table');
}
}
I do not add this seed call to seeds/DatabaseSeeder.php to avoid running it twice on a new installation.
The Artisan Command Solution
Create a new artisan command
php artisan make:command UpsertConfigurationTables
Paste this into the newly generated file: UpsertConfigurationTables.php
<?php
namespace App\Console\Commands;
use Exception;
use Illuminate\Console\Command;
class UpsertConfigurationTables extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'upsert:configuration';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Upserts the configuration tables.';
/**
* The models we want to upsert configuration data for
*
* #var array
*/
private $_models = [
'App\ExampleModel'
];
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
foreach ($this->_models as $model) {
// check that class exists
if (!class_exists($model)) {
throw new Exception('Configuration seed failed. Model does not exist.');
}
// check that seed data exists
if (!defined($model . '::CONFIGURATION_DATA')) {
throw new Exception('Configuration seed failed. Data does not exist.');
}
/**
* seed each record
*/
foreach ($model::CONFIGURATION_DATA as $row) {
$record = $this->_getRecord($model, $row['id']);
foreach ($row as $key => $value) {
$this->_upsertRecord($record, $row);
}
}
}
}
/**
* _fetchRecord - fetches a record if it exists, otherwise instantiates a new model
*
* #param string $model - the model
* #param integer $id - the model ID
*
* #return object - model instantiation
*/
private function _getRecord ($model, $id)
{
if ($this->_isSoftDeletable($model)) {
$record = $model::withTrashed()->find($id);
} else {
$record = $model::find($id);
}
return $record ? $record : new $model;
}
/**
* _upsertRecord - upsert a database record
*
* #param object $record - the record
* #param array $row - the row of update data
*
* #return object
*/
private function _upsertRecord ($record, $row)
{
foreach ($row as $key => $value) {
if ($key === 'deleted_at' && $this->_isSoftDeletable($record)) {
if ($record->trashed() && !$value) {
$record->restore();
} else if (!$record->trashed() && $value) {
$record->delete();
}
} else {
$record->$key = $value;
}
}
return $record->save();
}
/**
* _isSoftDeletable - Determines if a model is soft-deletable
*
* #param string $model - the model in question
*
* #return boolean
*/
private function _isSoftDeletable ($model)
{
$uses = array_merge(class_uses($model), class_uses(get_parent_class($model)));
return in_array('Illuminate\Database\Eloquent\SoftDeletes', $uses);
}
}
Populate $_models with the Eloquent models you want to seed.
Define the seed rows in the model: const CONFIGURATION_DATA
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class ExampleModel extends Model
{
use SoftDeletes;
const CONFIG_VALUE_ONE = 1;
const CONFIG_VALUE_TWO = 2;
const CONFIGURATION_DATA = [
[
'id' => self::CONFIG_VALUE_ONE,
'col1' => 'val1',
'col2' => 'val2',
'deleted_at' => false
],
[
'id' => self::CONFIG_VALUE_TWO,
'col1' => 'val1',
'col2' => 'val2',
'deleted_at' => true
],
];
}
Add the command to your Laravel Forge deployment script (or any other CI deployment script): php artisan upsert:configuration
Other noteworthy things:
Upsert Functionality: If you ever want to alter any of the seeded rows, simply update them in your model and it was update your database values next time you deploy. It will never create duplicate rows.
Soft-Deletable Models: Note that you define deletions by setting deleted_at to true or false. The Artisan command will handle calling the correct method to delete or recover your record.
Problems with other mentioned solutions:
Seeder: Running seeders in production is an abuse of the seeders. My concern would be that an engineer in the future would alter the seeders thinking that it's harmless since the documentation states that they are designed to seed test data.
Migrations: Seeding data in a migration is strange and an abuse of the purpose of the migration. It also doesn't let you update these values once your migration has been run.