Is that possible to initialize all the tenants DB at once and take all the records in a table? Laravel Multi tenant - laravel

As my diagram, I want to get all the records of table 1 in every tenants from the central domain side. Is that possible to initialize all the tenants at once and take all the records?
Or should I initialize tenants one by one and push records to an array?
I am using the tenancyforlaravel (archtechx/tenancy) package
Updated 1
In tenancyforlaravel tenancy package has a feature to connect tenant DB.It is called initialization. Normally It automatically identifies the database and initializes it by domain address. Also can manually initializes but need to give tenant id. Source link

Finally, I figure out how to do that after long days. It is very very easy with this package. There is a helper function. Only need to use this runForMultiple helper function. below has the example code,
tenancy()->runForMultiple(null, function (Tenant $tenant) {
foreach (Product::get() as $product) {
array_push($this->products, [
'shop_id' => $tenant->id,
'shop_name' => $tenant->name,
'id' => $product->id,
'name' => $product->name,
]);
}
});

Related

Multiple transaction create in accounts module using a laravel package

I am creating a double entry accounts website in laravel, transaction sale, purchase and cash or bank etc. And i have build the api for an payment website using laravel accounting package eloquent-ifrs , When I create api for add transaction, these trancation is multiple types.
I have all trasaction created expect 'contra entry' transaction because in this transaction main account type not valid for double entry for line item post ledgers.example:-
$contraEntry = ContraEntry::create([
'entity_id' => $entity,
'account_id' => $bankAccount->id,
]);
$zeroVat = DB::table('ifrs_vats')->where('entity_id', $entity)->whereNull('account_id')->first();
$assetAccount = Account::where('account_type', 'BANK')->first();
$contraEntryLineItem = LineItem::create([
'vat_id' => $zeroVat->id,
'account_id' => $assetAccount->id,
'vat_account_id' => $zeroVat->account_id
In this example of code contraEntry transaction created after that lineItem not created. Because A Transaction Main Account cannot be one of the Line Item Accounts.
I have sent parameter account_id for create first ContraEntry::create([]) and required account_type = "BANK" and account_type= "BANK" required in LineItem::create([]) but exist account id can't be send showing this error.
If any one have already worked on this Eloquent IFRS laravel package or working on this "Eloquent IFRS" accounting package.Help me out I really appreciate.

Best method to check for duplicates in store function prior to saving

I have a team-based app where one database services multiple teams. We have a custom fields table which allows for each team to create their own custom fields they want to create in addition to the global fields universally available to every team. Each custom field record has a name, type, and church_id field in the create.blade input form. Now with the nature of having separate teams, we need a system where they could create their own custom field that might share the same name of a custom field created and connected to another church team. This has been done and works just fine.
The problem is that we need to also make it so that only one custom field by a specific name can be created within the same church team. We do not want duplicate fields within the same church team. And herein lies my question, what is the best way in the store function to keep duplicates from occurring within the same team-based records. I have looked at firstOrNew, firstOrCreate, and updateOrCreate but which one is best suited to my need.
As I said, the fields we have in the form are 'name', 'type', and 'church_id' and these correspond to the custom field DB fields as 'name', 'type', and 'created_by_team_id'. What I need to have happen is for the system to check to see if there is a record matching the input 'name' that shares the same 'created_by_team_id' as the input 'church_id. If there is a record by that 'name' which also shares a marching 'id' then the system recognizes that as a duplicate and does NOT create a new record in the DB. But, if there is a record that shares the same 'name' but does not share the same 'created_by_team_id/church_id' then the system goes ahead and creates that new record because it is not a duplicate.
This is my create function:
{
if (! Gate::allows('custom_field_create')) {
return abort(401);
}
if (auth()->user()->role->contains(1)) {
$churches = Team::all();
$churchArr = array('empty' => 'Please select a church...');
foreach ($churches as $church) {
$churchArr[$church->id] = $church->name;
}
$churches->created_by_id = auth()->user()->id;
$church_id = null;
} else {
$churches = false;
$church_id = auth()->user()->team_id;
$churchArr = [];
}
return view('admin.custom_fields.create', compact('churches', 'churchArr', 'church_id'));
And this is my store function:
{
if (! Gate::allows('custom_field_create')) {
return abort(401);
}
$custom_field = CustomField::create($request->all());
$custom_field->created_by_team_id = $request->input('church_id');
$custom_field->save();
return redirect()->route('admin.custom_fields.index');
As I stated, I have been looking at and trying the firstOrNew, firstOrCreate, and updateOrCreate methods but all my attempts have been a failure. What would be the proper way to implement one of these methods to achieve my goals using my fields and DB criteria to avoid creating duplicate fields by the same name within the same team-based id?
I found the workable solution to be updateOrCreate. This allows the use of both 'name' and 'type' to be needing to be found to match. If either one is found but not the other it will create a new record. If both are found it just updates the record, which by nature avoids the duplicate creation. This allows the 'church_id' field to just added on as a filler rather than using it as one of the fields keyed on to.
$custom_field = CustomField::updateOrCreate(['name' => $request->input('name'), 'type' => $request->input('type')]);
$custom_field->created_by_team_id = $request->input('church_id');
$custom_field->save();

How to pass arguments to Laravel factories?

I have a users table and a one-to-zero/one relation with a businesses table (users.user_id => businesses.user_id). On my users table I have a discriminator which tells me if the user is of type business and therefore I need to have details on the businesses table as well.
I want to create my Users with my factory which currently is working and then only create business details where the discriminator points to a business account.
I have three options in my mind:
Create from users factory and then using '->each()' do some checks on the discriminator and create a new business user using a the factory. However I cannot pass to the business factory the user_id that the user was assigned.
First create the users. Then in my Business seeder, retrieve all Users that match a 'business' discriminator. Then for all of these users run a factory that creates the business details. But again, I would have to link somehow the user_id of the already create user with the business factory user_id.
In my business factory, create a new User and retrieve the id, thus making the link between users.user_id and business.user_id. However I am using a random generator for user.user_type so even if I have the businesses table filled it might be for users that have the discriminator as 'personal'.
Is there another way? Can I pass arguments from my Seeder to the factory?
The attributes you pass to the create function will be passed into your model definition callback as the second argument.
In your case you don't even need to access those attributes, since they'll automatically be merged in:
$business = factory(App\Business::class)->create();
factory(App\User::class, 5)->create([
'business_id' => $business->id,
]);
Adapt this to your needs.
My code for adding polymorphic 'Admin' users was:
// run model factory
factory(App\Admin::class, 3)->create()->each(function ($admin) {
$admin->user()->save(
// solved: https://laravel.com/docs/master/database-testing#using-factories (Overriding attributes)
factory(App\User::class)->make([
'userable_id' => $admin->id,
'userable_type' => App\Admin::class
])
);
});
Hope this helps.
Send attribute,
factory(App\User::class)->create(['businessId' => $businessId]);
Retrieve it,
$factory->define(App\User::class, function (Faker $faker, $businessInfo) {
//$businessInfo['businessId']
});

Laravel collection pluck method not working as expected

I've entered the fantastic world of Laravel and I am currently looking into seeding a database with fake data for testing.
I have a couple of tables I want to work with; projects and stories.
The stories table has the columns; id, name and project_id (which is a fk to the projects table).
My projects table is already populated with a list of 10 projects. Now I need to populate 100 stories with random projects associated. I have the approach below.
public function run()
{
DB::table('stories')->delete();
DB::statement('ALTER TABLE stories AUTO_INCREMENT = 1');
$faker = Faker::create();
foreach(range(1, 100) as $index)
{
Story::create([
'reference' => $faker->numberBetween(1, 9999),
'name' => $faker->sentence(6),
'project_id' => Project::orderBy(\DB::raw('RAND()'))->get()->first()->pluck('id')
]);
}
}
I don't know if this is the best way of doing what I need. However, when performing this code every story's project_id is set to 1; the first project's id.
When I perform the following command in tinker... It always returns 1 as the id.
Project::orderBy(\DB::raw('RAND()'))->get()->first()->pluck('id')
But when I perform the next command in tinker...
Project::orderBy(\DB::raw('RAND()'))->get()->first()
It returns a random project every time. Which is strange. Because if everything up to ->pluck() is working then pluck() should fetch that collected items id... Right? This is what the above command returns.
<App\Project #000000000c385908000000000de30942> {
id: 6,
name: "New Bernadetteton",
cover_photo_url: "/uploads/covers/horizon-grass.png",
created_at: "2015-07-08 16:32:15",
updated_at: "2015-07-08 16:32:15" }
See below screenshot for my terminal window to illustrate what I mean.
Here's what's happening:
With ->first() you get the actual project model
Then you call pluck('id') on it. BUT the Model class doesn't have that method.
So with, as with every method the Model doesn't know, it redirects the call to a new query builder instance of the model.
In the end, that call ends up here:
Illuminate\Database\Eloquent\Builder#value
public function value($column)
{
$result = $this->first(array($column));
if ($result) return $result->{$column};
}
As you can see, that method runs a new query, using first() and then returns the desired row.
Now what you actually want is either:
1. Don't use pluck at all
There isn't really a need to use that method, you can just access the model property:
'project_id' => Project::orderBy(\DB::raw('RAND()'))->first()->id
2. Use pluck, but do it right
'project_id' => Project::orderBy(\DB::raw('RAND()'))->pluck('id')
And btw, the main method is called value(). pluck() is just an alias. I recommend using value() in new code. It's possible that the alias will be removed some day. (Obviously just in a new release and with a note in the upgrade guide, so don't panic ;))

Laravel - Multi-tenancy - One database multiple tables

I want to create a multi tenancy application using Laravel. I am using the one database, multiple tenant tables database architecture.
I want to create a new set of tenant related tables dynamically, whenever someone registers a new tenant account.
Please consider the following:
tenants table holds all the client registrations. Each tenant will have dedicated tables only for them. In this example every tenant has their own dedicated customer table.
Ex:
tenant01's tables will have tenant01 prefix. (tenant01.customer - only tenant01's customers)
tenant02's tables will have tenant02 prefix. (tenant02.customer - only tenant02's customers)
I don't want to use multiple databases as they are costly and I don't want to use one table for all the tenants, as there will be lots of customers/products etc in the system.
I'm planning to identify the tenant at the logging process and set the tenant name(or a code/Id) in a session. ex: tenant440
After that, in all customer related eloquent model classes, I could dynamically append that prefix (ex: tenant440) into the table name like this:
<?php
class Customer extends Eloquent {
protected $tenant_name = //get name/code from the session. ex: tenant440
//table name will become tenant440.'customers'
protected $table = $tenant_name.'customers';
?>
Is this a correct way to achieve this? What is the simplest to do this? Do you know any kind of resources/packages/libraries for this?
Thanks in advance!
You can set tenant name as a prefix in your database file:
Config::set('database.connections.mysql.prefix',$tenantName); // assuming 'mysql' is the default database connection name
I recommend to put it inside a filter
// routes.php
Route::group(array('before'=>'setTablePrefix'), function($noob)
{
Route::resource('customers', 'CustomersController');
Route::controller('sales', 'SalesController');
});
Route::get('/login',array('as' => 'login', 'uses' => 'UserController#getLogin'));
// filters.php
Route::filter('setTablePrefix', function($route, $request)
{
if (!Session::has('prefixTable'))
App::abort(404);
Config::set('database.connections.mysql.prefix',Session::get('prefixTable'));
});
To get data from all tables, you probably need two queries (or one if you use Session)
$tenants = DB::table('tenants')->lists('name'); // you can set it in Session
if($tenants){
$allCustomers = DB::table($tenants[0].'.customers');
for ($i = 1; $i < count($tenants); $i++) {
$allCustomers->unionall(DB::table($tenants[$i].'.customers'));
}
var_dump($allCustomers->get());
}

Resources