localization Faker\Factory does not work in laravel - php

I have a Post Model with these fields :
post_id
post_title
post_content
post_content_full
author
Now I want to use laravel sedders and model factories to create fake fa_IR localized data and insert to posts table.
For that I wrote this in database/factories/ModelFactory.php:
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'post_title' => $faker->sentence,
'post_content' => $faker->paragraph,
'post_content_full' => $faker->paragraph(3),
'author' => $faker->name
];
});
Then I created a PostsTableSeeder class like this :
use Illuminate\Database\Seeder;
class PostsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run ()
{
factory(App\Post::class, 5)->create();
}
}
And in AppServiceProvider.php added below codes to register function :
$this->app->singleton(FakerGenerator::class, function () {
return FakerFactory::create('fa_IR');
});
But After running the seed , laravel uses default locale (en_US) and ignores fa_IR.
I do not know what else to do.
Update:
Even I changed in DEFAULT_LOCALE const vendor/fzaninotto/faker/src/Faker/Factory.php to fa_IR Nothing changed.

Not all faker methods are supported in every language, from what a quick lookup of the documentation says, the Company and Address provider are supported in the fa_IR localization

Try this way
$factory->define(App\Post::class, function () {
$faker = Faker\Factory::create('fa_IR');
return [
'post_title' => $faker->sentence,
'post_content' => $faker->paragraph,
'post_content_full' => $faker->paragraph(3),
'author' => $faker->name
];
});

You need to change the faker locale in your app config file.

First run this
php artisan make:factory PostFactory
Do like this
$faker = \Faker\Factory::create();
Then use like this
$sub_g->name = $faker->name();
$sub_g->country = $faker->country();
$sub_g->state = $faker->state;
Thank me later.

Related

Custom DatabaseSessionHandler in Laravel 8

I'm trying to make a custom DatabaseSessionHandler but it doesn't work as expected.
The idea is to make the table sessions polymorphic to allow the session from multiple models.
(To be honest, I'm not even sure it's possible)
But even before changing the table to be polymorphic, I'm trying to add a custom driver in order to manipulate the sessions.
The issue seems that my DatabaseSessionHandler, is not correctly called when I try to sign in.
config/auth.php
'guards' => [
'web' => [
'driver' => 'custom-session',
'provider' => 'users',
],
'screen' => [
'driver' => 'custom-session',
'provider' => 'screens',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => Domain\User\Models\User::class,
],
'screens' => [
'driver' => 'eloquent',
'model' => Domain\Screen\Models\Screen::class,
],
]
AuthServiceProvider.php
public function boot()
{
//This is how the session is normally registered: https://github.com/laravel/framework/blob/8.x/src/Illuminate/Session/SessionManager.php#L83
Session::resolved(function ($session) {
$session->extend('screen-session', function ($app) {
$table = $app['config']['session.table'];
$lifetime = $app['config']['session.lifetime'];
$connection = $app['db']->connection($app['config']['session.connection']);
return new \Support\Session\DatabaseSessionHandler($connection, $table, $lifetime, $app);
});
});
// This is how the driver "session" is normally registered: https://github.com/laravel/framework/blob/8.x/src/Illuminate/Auth/AuthManager.php#L121
Auth::resolved(function ($auth) {
$auth->extend('custom-session', function ($app, $name, array $config) {
$provider = Auth::createUserProvider($config['provider']);
$guard = new SessionGuard($name, $provider, $app->session->driver('screen-session'));
if (method_exists($guard, 'setCookieJar')) {
$guard->setCookieJar($this->app['cookie']);
}
if (method_exists($guard, 'setDispatcher')) {
$guard->setDispatcher($this->app['events']);
}
if (method_exists($guard, 'setRequest')) {
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
}
return $guard;
});
});
}
DatabaseSessionHandler.php
This is currently a copy/past of the existing one that is used by session except the namespace. https://github.com/laravel/framework/blob/8.x/src/Illuminate/Session/DatabaseSessionHandler.php
I don't have any error message.
When I try to sign in (auth()->guard('web')->login($user))
It validate the login
I know it uses my DatabaseSessionHandler to destroy the current
session in the table.
The session ID is regenerated and I'm not logged in
An another scenario;
I try to sign in with the "remember me"
It validate the login
Somehow, the session is updated in the database with the current user_id but it doesn't seems that my DatabaseSessionHandler have been used
I'm logged in
Removing the screen from the guards and providers to only keep web and users, doesn't change anything.
Finally solved it. As always, it's a simple mistake.
I add to change in .env file
SESSION_DRIVER=screen-session
And now it's working as expected.
Now, also to answer my another question
The idea is to make the table sessions polymorphic to allow the session from multiple models. (To be honest, I'm not even sure it's possible)
From my quick tests it looks realizable. Here's what I did;
create_sessions_table.php
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->nullableMorphs('authenticable');
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity')->index();
});
DatabaseSessionHandler.php
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Session\DatabaseSessionHandler as BaseDatabaseSessionHandler;
class DatabaseSessionHandler extends BaseDatabaseSessionHandler
{
/**
* Get the currently authenticated user's type.
*
* #return mixed
*/
protected function userType()
{
$user = $this->container->make(Guard::class)->user();
return optional($user)->getMorphClass();
}
/**
* Add the user information to the session payload.
*
* #param array $payload
* #return $this
*/
protected function addUserInformation(&$payload)
{
if ($this->container->bound(Guard::class)) {
$payload['authenticable_id'] = $this->userId();
$payload['authenticable_type'] = $this->userType();
}
return $this;
}
}
Note: If you're using Laravel jetstream, you will probably need a custom LogoutOtherBrowserSessionsForm livewire/inertiajs component since this one is based and hardcoded with the database session and looks for user_id column

Laravel - Polymorphic relationship not working

So I have the following code:
class PageSection extends Model {
protected $table = "PageSection";
const TYPE_CURATED = 0;
const TYPE_AUTOMATED = 1;
public function list() {
return $this->morphTo('list', 'entity_type', 'id_Entity');
}
}
then in AppServiceProvider.php I have the following:
use App\PageSection;
use App\PageSectionGroup;
use App\PageListEntry;
use App\RSSFeed;
use App\Shortcut;
use App\RSSEpisode;
use App\PageList;
use App\AutomatedList;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
Relation::morphMap([
'Section' => PageSection::class,
'SectionGroup' => PageSectionGroup::class,
PageSection::TYPE_CURATED => PageList::class,
PageSection::TYPE_AUTOMATED => AutomatedList::class,
PageListEntry::TYPE_FEED => RSSFeed::class,
PageListEntry::TYPE_SHORTCUT => Shortcut::class,
PageListEntry::TYPE_EPISODE => RSSEpisode::class
]);
}
Then I have a test route in my api routes that checks to see if the list is being loaded, and it returns null: (Yes, I've verified that the section itself exists)
Route::get('/test', function() {
$section = PageSection::with(['list', 'type'])->find(1);
// this returns null
return $section->list;
});
My database schema for PageSection is such that entity_type tells what the model is, and id_Entity is the foreign key for that model, which is named 'id' on the referenced table.
The other relations defined in morphMap are working properly, yet for some reason the list() relationship in PageSection is not. I'm not sure what I'm doing wrong here.. any help would be appreciated.
Ok, so I figured out what was going on. It's probably a bug with Laravel's morphMap. I was using 0 for the PageSection::TYPE_CURATED constant, which is a falsey value. When I switched to:
Relation::morphMap([
'PageList' => PageList::class,
'AutomatedList' => AutomatedList::class,
'Section' => PageSection::class,
'SectionGroup' => PageSectionGroup::class,
PageListEntry::TYPE_FEED => RSSFeed::class,
PageListEntry::TYPE_SHORTCUT => Shortcut::class,
PageListEntry::TYPE_EPISODE => RSSEpisode::class
]);
it all worked fine. Seems like Laravel doesn't like the value 0.

Laravel Spark, Swap/Interact, and Private Variables

Using Laravel Spark, if I wanted to swap in a new implementation for the configureTeamForNewUser, at first it looks like it's possible because of the Spark::interact call here
#File: spark/src/Interactions/Auth/Register.php
Spark::interact(self::class.'#configureTeamForNewUser', [$request, $user]);
i.e. the framework calls configureTeamForNewUser using Spark::interact, which means I can Spark::swap it.
However, if I look at the configureTemForNewUser method itself
#File: spark/src/Interactions/Auth/Register.php
public function configureTeamForNewUser(RegisterRequest $request, $user)
{
if ($invitation = $request->invitation()) {
Spark::interact(AddTeamMember::class, [$invitation->team, $user]);
self::$team = $invitation->team;
$invitation->delete();
} elseif (Spark::onlyTeamPlans()) {
self::$team = Spark::interact(CreateTeam::class, [
$user, ['name' => $request->team, 'slug' => $request->team_slug]
]);
}
$user->currentTeam();
}
This method assigns a value to the private $team class property. It's my understanding that if I use Spark::swap my callback is called instead of the original method. Initial tests confirm this. However, since my callback can't set $team, this means my callback would change the behavior of the system in a way that's going to break other spark functionality.
Is the above a correct understanding of the system? Or am I missing something, and it would be possible to swap in another function call (somehow calling the original configureTeamForNewUser)?
Of course, you can swap this configureTeamForNewUser method. Spark create a team for a user at the registration. You have to add the swap method inside the Booted() method of App/Providers/SparkServiceProvider.php class.
in the top use following,
use Laravel\Spark\Contracts\Interactions\Auth\Register;
use Laravel\Spark\Contracts\Http\Requests\Auth\RegisterRequest;
use Laravel\Spark\Contracts\Interactions\Settings\Teams\CreateTeam;
use Laravel\Spark\Contracts\Interactions\Settings\Teams\AddTeamMember;
In my case I want to add new field call "custom_one" to the teams table. Inside the booted() method, swap the method as bellow.
Spark::swap('Register#configureTeamForNewUser', function(RegisterRequest $request, $user){
if ($invitation = $request->invitation()) {
Spark::interact(AddTeamMember::class, [$invitation->team, $user]);
self::$team = $invitation->team;
$invitation->delete();
} elseif (Spark::onlyTeamPlans()) {
self::$team = Spark::interact(CreateTeam::class, [ $user,
[
'name' => $request->team,
'slug' => $request->team_slug,
'custom_one' => $request->custom_one,
] ]);
}
$user->currentTeam();
});
In order to add a new custom_one field, I had to swap the TeamRepository#createmethod as well. After swapping configureTeamForNewUser method, swap the TeamRepository#create method onside the booted(),
Spark::swap('TeamRepository#create', function ($user, $data) {
$attributes = [
'owner_id' => $user->id,
'name' => $data['name'],
'custom_one' => $data['custom_one'],
'trial_ends_at' => Carbon::now()->addDays(Spark::teamTrialDays()),
];
if (Spark::teamsIdentifiedByPath()) {
$attributes['slug'] = $data['slug'];
}
return Spark::team()->forceCreate($attributes);
});
Then proceed with your registration.
See Laravel Spark documentation

Slim: Multiple GET names for 1 url

I am trying to add a video section in my website using twig, slim, and eloquent. Now here is what I have for my route for my 'watch' pages
<?php
$app->get('/watch/:series/:episode', function($series, $episode) use($app){
$video = $app->video->where('series', $series)->where('episode', $episode)->first();
$app->render('videos/watch.php', [
'video' => $video
]);
})->name('videos.watch');
now what I want to do is also have it to where when if they do not specifiy an episode number (i.e. type in 'watch/seriesName/') it defaults to the lowest episode number. My database structure is 'id', 'title', 'series', 'episode' (all that really matter to this issue).
In the midst of asking this question I actually figured it out. I will answer my own question so that others who may have the same problem may know what I did.
Here is my video class just in case anyone is interested in how to utilize my video class (not sure if it was the best way of going about handling videos but it works)
<?php
namespace BiosystemStudios\Video;
use Illuminate\Database\Eloquent\Model as Eloquent;
use JBBCode\Parser;
use JBBCode\DefaultCodeDefinitionSet;
class Video extends Eloquent
{
protected $table = 'content_video';
protected $fillable = [
'title',
'series',
'episode',
'location',
'description',
'category',
'is_youtube',
'youtube_link',
'views'
];
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
$parser = new Parser();
$parser->addCodeDefinitionSet(new DefaultCodeDefinitionSet());
$text = $this->description;
$parser->parse($text);
return $parser->getAsHtml();
}
public function getLocation()
{
return $this->location;
}
Rename the first get request to "videos.watch.episode" then create this get request:
$app->get('/watch/:series/', function($series) use($app){
$video = $app->video->where('series', $series)->first();
$app->render('videos/watch.php', [
'video' => $video
]);
})->name('videos.watch');

Laravel - Seeding Relationships

In Laravel, database seeding is generally accomplished through Model factories. So you define a blueprint for your Model using Faker data, and say how many instances you need:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$user = factory(App\User::class, 50)->create();
However, lets say your User model has a hasMany relationship with many other Models, like a Post model for example:
Post:
id
name
body
user_id
So in this situation, you want to seed your Posts table with actual users that were seeded in your Users table. This doesn't seem to be explicitly discussed, but I did find the following in the Laravel docs:
$users = factory(App\User::class, 3)
->create()
->each(function($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
So in your User factory, you create X number of Posts for each User you create. However, in a large application where maybe 50 - 75 Models share relationships with the User Model, your User Seeder would essentially end up seeding the entire database with all it's relationships.
My question is: Is this the best way to handle this? The only other thing I can think of is to Seed the Users first (without seeding any relations), and then pull random Users from the DB as needed while you are seeding other Models. However, in cases where they need to be unique, you'd have to keep track of which Users had been used. Also, it seems this would add a lot of extra query-bulk to the seeding process.
You can use saveMany as well. For example:
factory(User::class, 10)->create()->each(function ($user) {
$user->posts()->saveMany(factory(Posts::class, 5)->make());
});
You can do this using closures within the ModelFactory as discussed here.
This solution works cleanly and elegantly with seeders as well.
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'body' => $faker->paragraph(1),
'user_id' => function() {
return factory(App\User::class)->create()->id;
},
];
});
For your seeder, use something simple like this:
//create 10 users
factory(User::class, 10)->create()->each(function ($user) {
//create 5 posts for each user
factory(Post::class, 5)->create(['user_id'=>$user->id]);
});
NOTE: This method does not create unneeded entries in the database, instead the passed attributes are assigned BEFORE the creation of associated records.
Personally I think one Seeder class to manage these relations is nicer then separated seeder classes, because you have all the logic in one place, so in one look you can see what is going on. (Anyone that knows a better approach: please share) :)
A solution might be: one DatabaseSeeder and private methods within the class to keep the 'run' method a bit cleaner. I have this example below, which has a User, Link, LinkUser (many-to-many) and a Note (many-to-one).
For the many-to-many relations I first create all the Links, and get the inserted ids. (since the ids are auto-inc I think the ids could be fetched easier (get max), but doesn't matter in this example). Then create the users, and attach some random links to each user (many-to-many). It also creates random notes for each user (many-to-one example). It uses the 'factory' methods.
If you replace the 'Link' for your 'Post' this should work. (You can remove the 'Note' section then...)
(There is also a method to make sure you have 1 valid user with your own login credentials.)
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
// Create random links
factory(App\Link::class, 100)->create();
// Fetch the link ids
$link_ids = App\Link::all('id')->pluck('id')->toArray();
// Create random users
factory(App\User::class, 50)->create()->each(function ($user) use ($link_ids) {
// Example: Many-to-many relations
$this->attachRandomLinksToUser($user->id, $link_ids);
// Example: Many-to-one relations
$this->createNotesForUserId( $user->id );
});
// Make sure you have a user to login with (your own email, name and password)
$this->updateCredentialsForTestLogin('john#doe.com', 'John Doe', 'my-password');
}
/**
* #param $user_id
* #param $link_ids
* #return void
*/
private function attachRandomLinksToUser($user_id, $link_ids)
{
$amount = random_int( 0, count($link_ids) ); // The amount of links for this user
echo "Attach " . $amount . " link(s) to user " . $user_id . "\n";
if($amount > 0) {
$keys = (array)array_rand($link_ids, $amount); // Random links
foreach($keys as $key) {
DB::table('link_user')->insert([
'link_id' => $link_ids[$key],
'user_id' => $user_id,
]);
}
}
}
/**
* #param $user_id
* #return void
*/
private function createNotesForUserId($user_id)
{
$amount = random_int(10, 50);
factory(App\Note::class, $amount)->create([
'user_id' => $user_id
]);
}
/**
* #param $email
* #param $name
* #param $password
* #return void
*/
private function updateCredentialsForTestLogin($email, $name, $password)
{
$user = App\User::where('email', $email)->first();
if(!$user) {
$user = App\User::find(1);
}
$user->name = $name;
$user->email = $email;
$user->password = bcrypt($password); // Or whatever you use for password encryption
$user->save();
}
}
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'body' => $faker->paragraph(1),
'user_id' => factory(App\User::class)->create()->id,
];
});
So now if you do this factory(App\Post::class, 4)->create() it will create 4 different posts and in the process also create 4 different users.
If you want the same user for all the posts what I usually do is:
$user = factory(App\User::class)->create();
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]);
I want to share the approach i've taken for insert many posts to many users:`
factory(App\User::class, 50)->create()
->each(
function ($u) {
factory(App\Post::class, 10)->create()
->each(
function($p) use (&$u) {
$u->posts()->save($p)->make();
}
);
}
);
`
This workaround worked for me after being all day long looking for a way to seed the relationship
this worked for me in laravel v8
for ($i=0; $i<=2; $i++) {
$user = \App\Models\User::factory(1)->create()->first();
$product = \App\Models\Product::factory(1)->create(['user_id' => $user->id])->first();
}
I use a custom made relateOrCreate function that finds a random entry of that model in the database. If none exist, it creates a new one:
function relateOrCreate($class) {
$instances = $class::all();
$instance;
if (count($instances) > 0) {
$randomIndex = rand(0, (count($instances) - 1));
$instance = $instances[$randomIndex];
}
else {
$instance = $class::factory()->create();
}
return $instance;
}
Then I use it like so:
$relatedUser = relateOrCreate(User::class);
return [
'user_id' => $relatedUser->id,
// ...
];

Categories