Laravel spatie translatable and sluggable (translatable slugs) - php

So I have just discovered the package laravel-sluggable and I am trying to setup translatable slugs. I got an error with the route model binding, error 404. So I created a simple migration, simple model and simple route, but the same error occurs. My database supports json.
My migration:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->json('name');
$table->json('slug');
$table->timestamps();
});
}
My model:
<?php
namespace App\Models;
use Spatie\Sluggable\SlugOptions;
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
use Spatie\Sluggable\HasTranslatableSlug;
class Post extends Model
{
use HasTranslations, HasTranslatableSlug;
public $translatable = ['name', 'slug'];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions() : SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug';
}
}
Artisan tinker shows me that the Post I created has been added and it looks like this:
+"name": "{"nl": "test"}",
+"slug": "{"nl": "test"}",
My locale is indeed nl at the moment but browsing to the following route gives me a 404 error:
use App\Models\Post;
Route::get('posts/{post}', function (Post $post) {
dd($post);
});
My question is, what am I missing here? Regular slugs with this package are working like a charm. Also, the translatable package is working like a charm. It is just the translated slugs I am having an issue with right now.

With a fresh Laravel 9 installation it worked like a charm and thus I have upgraded my Laravel 8 to Laravel 9 and it is working now.

Related

Laravel Query Builder hard coded data is not inserted with post method

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.

Laravel 9: Implicit model binding not working on nested one-to-many relationship

I have a problem using Laravel's implicit model binding using a nested route and a custom key (not ID). I have these example models:
Categories (migration):
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('name')->unique();
});
}
Posts (migration):
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('category_id')->nullable()->constrained();
$table->timestamps();
$table->string('text');
});
}
The relationship is One-To-Many:
A Post has one category.
A category can have multiple Posts (Posts that have the same category).
The model classes like:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['category'];
/**
* The attributes that are mass assignable.
*
* #var array<int, string>
*/
protected $fillable = [
'category_id',
];
use HasFactory;
public function category() {
return $this->belongsTo(Category::class);
}
}
and
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
public function posts()
{
return $this->hasMany(Post::class);
}
}
And I am using these routes to create categories/posts and attach a post to a category:
api.php:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
use App\Http\Controllers\CategoryController;
Route::apiResource('posts', PostController::class);
Route::apiResource('categories', CategoryController::class);
Route::get('posts/{post}/category', [PostController::class, 'getCategory']);
Route::post('posts/{post}/category/{category:name}', [PostController::class, 'addCategory']);
The PostController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
use App\Models\Category;
use Illuminate\Database\QueryException;
class PostController extends Controller
{
/**
* Other functions for other routes
*
*
*/
public function addCategory(Request $request, Post $post, Category $category)
{
$category->posts()->save($post);
$post->category()->associate($category);
$post->save();
return $post;
}
public function removeCategory(Request $request, Post $post, Category $category)
{
$post->category()->dissociate($post);
$post->save();
return response(['message'=>'Category removed.'], 200);
}
public function getCategory(Request $request, Post $post, Category $category)
{
return $post->category;
}
}
Now I create a Post and a category (works fine) and want to associate a post with a category using:
POST-Request to: http://localhost/api/posts/5/category/life
Results in:
Expected response status code [200] but received 500.
The following exception occurred during the request:
BadMethodCallException: Call to undefined method App\Models\Post::categories() in /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:71
Why is Laravel trying to call that method on a one-to-many relationship?
A Post with ID 5 and a category with name attribute with string value "life" exists in the database.
But I always get a 404 error using the route. If I use the category-ID instead it is working fine. So the implicit model binding is not working for the nested route if using a custom key?
If I rewrite the 'addCategory'-method it is working fine:
public function addCategory(Request $request, Post $post, $name)
{
$category = Category::where('name', '=', $name)->firstOrFail();
$category->posts()->save($post);
$post->category()->associate($category);
$post->save();
return $post;
}
But I think Laravel's implicit binding should exactly do this automatically?
Anyone knows what is going wrong here?
The way laravel works with nested model bindings is when you have more than one wildcard it will assume the last model wildcard belongs to the second last model wildcard, and this one belongs to the third to last place model wildcard and so on... to the second model wildcard belongs to the first (that's why it calls a has many method), so the first has many seconds, that has many thirds, I hope you understood, the thing is, the relation has to already exist in order to do that, so you can't put post before category, because a category has many posts. You just have to remember always put the one model that has many before the one that belongs to, even if the instances are not related they will be found. So just swap the endpoint route like this Route::post('category/{category:name}/posts/{post}', [PostController::class, 'addCategory']);, and then swap the addCategory method arguments. I'd recommend you doing that with your request, since you're not doing anything with it, it is weird seeing you have a post method with no request body
The doc stated that:
... Laravel will automatically inject the model instance that has an ID matching the corresponding value from the request URI. If a matching model instance is not found in the database, a 404 HTTP response will automatically be generated.
So I think what happened is expected.

Acces to data using relationships Laravel

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);
.

Laravel Base table or view not found

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

How can I encrypt all ids in url in Laravel 5.2

I need to do all url ids encrypted like :
user/edit/1
items/edit/35
posts/details/52
to
user/edit/sdfjk54dfds
items/edit/sdfjk54dfds
posts/details/sdfjk5s4dfds
there is lots of areas like blade files and in controllers that id used url('items/edit/2') and also in controller some function are passed by objects like public function itemedit(Items $items).
I tried $encrypt_val = Crypt::encrypt($value) and $decrypt_val = Crypt::decrypt($encrypt_val ); but I need to do it everywhere.
There is any short way or Middleware function to do it ?
Use Laravel Hashids
You can encode id like below
$encoded_id = Hashids::encode($id);
Your URL will be like below
<url>/users/edit/sdfjk54dfds
Hash ID installation guide
https://github.com/vinkla/laravel-hashids
You can use Uuid instead of using integer id. For this please follow the instruction:
Just create a trait:
trait Uuids
{
/**
* Boot function from laravel.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getKeyName()} = Uuid::generate()->string;
});
}
}
and in your model use above trait:
use SomeNamespcaeOfTrait;
class User extends Eloquent
{
use Uuids;
/**
* #var bool
*/
public $incrementing = false;
}
and in your migration use uuid instead of integer.
There's a package called Laravel HashSlug that acts as desired. Similarly to the one in sumit's answer, it's built on Hashids, but design specially to work with urls.
Using the above package all you need to do is add a trait and typehint in controller:
class Post extends Model {
use HasHashSlug;
}
// routes/web.php
Route::resource('/posts', 'PostController');
// app/Http/Controllers/PostController.php
public function show(Post $post){
return view('post.show', compact('post'));
}

Categories