Dynamic seeding in laravel 5.4 - php

I'm extremely new to laravel and having trouble with this seeder. I feel like it should be an easy thing to do, I just don't have the right tools for it yet.
I need to populate a column with data based on a different column. More specifically, I want to take the employee_id, find where their location is in the employee table, and populate that id in the application table. This is what I have so far:
use Illuminate\Database\Seeder;
use App\Employee;
use App\Application;
class ApplicationEmployeeLocationTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$applications= Application::all();
foreach ($applications as $application)
{
$location_id = Employee::whereId($application->employee_id)->pluck('location_id')->first();
}
}
}
This returns all the ids I'm looking for as a string, but how do I put these values into location_idin the applications table? Running this seeder doesn't produce an error, but it doesn't do anything either.
Let me know if there's any other information you need, and thanks in advance.

It would be easier if you just pulled the location_id from an object. Also, to eliminate multiple hits to the database in that loop, I suggest you pull in a collection of employees (pulling from the DB only once) before the loop, and then find the right location for the employee from the collection.
The way you have it now, you go to the DB on every looped application to draw an employee. Having a collection before the loop will eliminate all those pulls.
I'll break it out into a few steps below for clarity, but you can tighten this if you wish:
$applications= Application::all();
$employees = Employee::all();
foreach ($applications as $application)
{
$emp = $employees->where('id', $application->id)->first()
$location_id = $emp->location_id;
}
To put the location for the application back into the database, you can simply save it to the application object:
foreach ($applications as $application)
{
$emp = $employees->where('id', $application->id)->first()
$location_id = $emp->location_id;
$application->location_id = $location_id;
$application->save();
}
The above is writing to the DB each time, which I was trying to avoid, but once you get this working, you can always investigate adding those applications to a collection and then saving the collection. But this should get you where you want to be.

Related

Laravel 8 - how to fetch data from the database using an object as a "where" condition?

im new in laravel programming and im trying to do something with laravel Eloquent.
So i have 3 tables, dev, dev_project(many to many) and project.
i need to see in which projects the dev is part of, using the dev_project table, and get the corresponding id from projects.
after that i need to go to the table project, and use the id that I got from the many-to-many table to get the corresponding name of the projects in which that same dev is part of.
First im fetching data from my DB into a variable
$devproject = DB::table('dev_project')->where('id_dev', '=', Auth::user()->id)->get();
And then Im trying to get the data from the project table where the id is the same from the one that i fetched before
return Project::where('id', $dev_project->id_project);
The problem here is that the variable dev_project is an object, and i cant use a foreach and store the data that i want to return inside an array.
So how can I get all the data that i want in the same object.
I appreciate any help, Thank you.
Here, $devproject is a Collection, not a single row. $dev_project->id_project is not a valid property.
If you want the first result's id_project, then the syntax is $devproject->first()->id_project.
If you want a Collection of each of $devproject 's id_project, then the syntax should be $devproject->pluck('id_project').
With that in mind, your query should either be
return Project::where('id', $dev_project->first()->id_project);
or
return Project::whereIn('id', $dev_project->pluck('id_project'));
As harish durga suggests, a relationship would save you the trouble of writin these queries.
# Dev Model
public function projects()
{
return $this->belongsToMany(Project::class, 'dev_project', 'id_project', 'id_dev');
}
# Project Model
public function devs()
{
return $this->belongsToMany(Dev::class, 'dev_project', 'id_dev', 'id_project');
}
# Get all devs with associated projects:
$devs = Dev::with('projects')->get();
foreach ($devs as $dev) {
echo $dev->...
foreach ($dev->projects as $project) {
echo $project->...
}
}

Laravel How do I access differents models through a single models with polymorphic relationships?

I'm trying to develop an app where one can make his own program with different activities. So far I have 3 tables, the first one is the program table :
// Program table
id - integer
title - string
Then I have a program_activities table
// Program_Activities Table
id - integer
program_id - integer
activity_id - integer
activity_type - string
And then I have all my different activities (Sightseeing, Camping, etc...)
How can I access all activities through the Program model? Something like this
// Program.php
Class Program extends Model
{
[...]
public function activities()
{
// retrurn all activities
}
}
So that $session->activities would list me all the activities from that session?
Thank you in advance.
Okay, so I "kinda" resolved my problem, but I don't really think it's good practice nor do I think it's optimal. Nontheless, here's my solution. If somebody find a better way, please show me.
// Program.php
[...]
/**
* Get the activities of the program.
*/
public function activities()
{
$program_activities = $this->hasMany('App\ProgramActivity')->get();
$activities = [];
foreach ($program_activities as $program_activity){
$className = $program_activity->activity_type;
$activity = $className::find($program_activity->activity_id);
$activities[] = $activity;
}
$activities = collect($activities);
return $activities;
}
That way, when I do $program->activities(), I do get all the activities of the program.
Even though I think there is a better way to do this, or I think Laravel might cover this with one of it's many relations, I didn't found any. If you have a better answer, then please post it =)

Advanced Laravel merged data/models - can it be done at model level?

We have a COMMON database and then tenant databases for each organization that uses our application. We have base values in the COMMON database for some tables e.g.
COMMON.widgets. Then in the tenant databases, IF a table called modified_widgets exists and has values, they are merged with the COMMON.widgets table.
Right now we are doing this in controllers along the lines of:
public function index(Request $request)
{
$widgets = Widget::where('active', '1')->orderBy('name')->get();
if(Schema::connection('tenant')->hasTable('modified_widgets')) {
$modified = ModifiedWidget::where('active', '1')->get();
$merged = $widgets->merge($modified);
$merged = array_values(array_sort($merged, function ($value) {
return $value['name'];
}));
return $merged;
}
return $countries;
}
As you can see, we have model for each table and this works OK. We get the expected results for GET requests like this from controllers, but we'd like to merge at the Laravel MODEL level if possible. That way id's are linked to the correct tables and such when populating forms with these values. The merge means the same id can exist in BOTH tables. We ALWAYS want to act on the merged data if any exists. So it seems like model level is the place for this, but we'll try any suggestions that help meet the need. Hope that all makes sense.
Can anyone help with this or does anyone have any ideas to try? We've played with overriding model constructors and such, but haven't quite been able to figure this out yet. Any thoughts are appreciated and TIA!
If you put this functionality in Widget model you will get 2x times of queries. You need to think about Widget as an instance, what I am trying to say is that current approach does 2 queries minimum and +1 if tenant has modified_widgets table. Now imagine you do this inside a model, each Widget instance will pull in, in a best case scenario its equivalent from different database, so for bunch of Widgets you will do 1 (->all())+n (n = number of ModifiedWidgets) queries - because each Widget instance will pull its own mirror if it exists, no eager load is possible.
You can improve your code with following:
$widgets = Widget::where('active', '1')->orderBy('name')->get();
if(Schema::connection('tenant')->hasTable('modified_widgets')) {
$modified = ModifiedWidget::where('active', '1')->whereIn('id', $widgets->pluck('id'))->get(); // remove whereIn if thats not the case
return $widgets->merge($modified)->unique()->sortBy('name');
}
return $widgets;
OK, here is what we came up with.
We now use a single model and the table names MUST be the same in both databases (setTable does not seem to work even though in exists in the Database/Eloquent/Model base source code - that may be why it's not documented). Anyway = just use a regular model and make sure the tables are identical (or at least the fields you are using are):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Widget extends Model
{
}
Then we have a generic 'merge controller' where the model and optional sort are passed in the request (we hard coded the 'where' and key here, but they could be made dynamic too). NOTE THIS WILL NOT WORK WITH STATIC METHODS THAT CREATE NEW INSTANCES such as $model::all() so you need to use $model->get() in that case:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class MergeController extends Controller
{
public function index(Request $request)
{
//TODO: add some validations to ensure model is provided
$model = app("App\\Models\\{$request['model']}");
$sort = $request['sort'] ? $request['sort'] : 'id';
$src_collection = $model->where('active', '1')->orderBy('name')->get();
// we setup the tenants connection elsewhere, but use it here
if(Schema::connection('tenant')->hasTable($model->getTable())) {
$model->setConnection('tenant');
$tenant_collection = $model->get()->where('active', '1');
$src_collection = $src_collection->keyBy('id')->merge($tenant_collection->keyBy('id'))->sortBy('name');
}
return $src_collection;
}
}
If you dd($src_collection); before returning it it, you will see the connection is correct for each row (depending on data in the tables). If you update a row:
$test = $src_collection->find(2); // this is a row from the tenant db in our data
$test->name = 'Test';
$test->save();
$test2 = $src_collection->find(1); // this is a row from the tenant db in our data
$test2->name = 'Test2'; // this is a row from the COMMON db in our data
$test2->save();
dd($src_collection);
You will see the correct data is updated no matter which table the row(s) came from.
This results in each tenant being able to optionally override and/or add to base table data without effecting the base table data itself or other tenants while minimizing data duplication thus easing maintenance (obviously the table data and population is managed elsewhere just like any other table). If the tenant has no overrides then the base table data is returned. The merge and custom collection stuff have minimal documentation, so this took some time to figure out. Hope this helps someone else some day!

Check if belongsToMany relation exists - Laravel

Two of my tables (clients and products) have a ManyToMany relation using Laravel's blongToMany and a pivot table.
Now I want to check if a certain client has a certain product.
I could create a model to check in the pivot table but since Laravel does not require this model for the belongsToMany method I was wondering if there is another way to check if a certain relationship exists without having a model for the pivot table.
I think the official way to do this is to do:
$client = Client::find(1);
$exists = $client->products->contains($product_id);
It's somewhat wasteful in that it'll do the SELECT query, get all results into a Collection and then finally do a foreach over the Collection to find a model with the ID you pass in. However, it doesn't require modelling the pivot table.
If you don't like the wastefulness of that, you could do it yourself in SQL/Query Builder, which also wouldn't require modelling the table (nor would it require getting the Client model if you don't already have it for other purposes:
$exists = DB::table('client_product')
->whereClientId($client_id)
->whereProductId($product_id)
->count() > 0;
The question is quite old but this may help others looking for a solution:
$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();
No "wastefulness" as in #alexrussell's solution and the query is more efficient, too.
Alex's solution is working one, but it will load a Client model and all related Product models from DB into memory and only after that, it will check if the relationship exists.
A better Eloquent way to do that is to use whereHas() method.
1. You don't need to load client model, you can just use his ID.
2. You also don't need to load all products related to that client into memory, like Alex does.
3. One SQL query to DB.
$doesClientHaveProduct = Product::where('id', $productId)
->whereHas('clients', function($q) use($clientId) {
$q->where('id', $clientId);
})
->count();
Update: I did not take into account the usefulness of checking multiple relations, if that is the case then #deczo has a way better answer to this question. Running only one query to check for all relations is the desired solution.
/**
* Determine if a Client has a specific Product
* #param $clientId
* #param $productId
* #return bool
*/
public function clientHasProduct($clientId, $productId)
{
return ! is_null(
DB::table('client_product')
->where('client_id', $clientId)
->where('product_id', $productId)
->first()
);
}
You could put this in you User/Client model or you could have it in a ClientRepository and use that wherever you need it.
if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
return 'Awesome';
}
But if you already have defined the belongsToMany relationship on a Client Eloquent model, you could do this, inside your Client model, instead:
return ! is_null(
$this->products()
->where('product_id', $productId)
->first()
);
#nielsiano's methods will work, but they will query DB for every user/product pair, which is a waste in my opinion.
If you don't want to load all the related models' data, then this is what I would do for a single user:
// User model
protected $productIds = null;
public function getProductsIdsAttribute()
{
if (is_null($this->productsIds) $this->loadProductsIds();
return $this->productsIds;
}
public function loadProductsIds()
{
$this->productsIds = DB::table($this->products()->getTable())
->where($this->products()->getForeignKey(), $this->getKey())
->lists($this->products()->getOtherKey());
return $this;
}
public function hasProduct($id)
{
return in_array($id, $this->productsIds);
}
Then you can simply do this:
$user = User::first();
$user->hasProduct($someId); // true / false
// or
Auth::user()->hasProduct($someId);
Only 1 query is executed, then you work with the array.
The easiest way would be using contains like #alexrussell suggested.
I think this is a matter of preference, so unless your app is quite big and requires a lot of optimization, you can choose what you find easier to work with.
Hello all) My solution for this problem: i created a own class, extended from Eloquent, and extend all my models from it. In this class i written this simple function:
function have($relation_name, $id) {
return (bool) $this->$relation_name()->where('id','=',$id)->count();
}
For make a check existing relation you must write something like:
if ($user->have('subscribes', 15)) {
// do some things
}
This way generates only a SELECT count(...) query without receiving real data from tables.
To check the existence of a relationship between 2 models, all we need is a single query against the pivot table without any joins.
You can achieve it using the built-in newPivotStatementForId method:
$exists = $client->products()->newPivotStatementForId($product->id)->exists();
use trait:
trait hasPivotTrait
{
public function hasPivot($relation, $model)
{
return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
}
}
.
if ($user->hasPivot('tags', $tag)){
// do some things...
}
This has time but maybe I can help someone
if($client->products()->find($product->id)){
exists!!
}
It should be noted that you must have the product and customer model, I hope it helps,

what is the common practice on doing oo in db?

Here is situation.... ...
I have a DBManager, which is implement a DBInterface, in the DBInterface, I got 4 method:
-create(DBCmd);
-read(DBCmd);
-update(DBCmd);
-delete(DBCmd);
The DBCmd object is responsible for generate the SQL statement, and the DBCmd requires an object in sql statement:
class DBCmd{
public _constructor($aObj){
}
public executeCreate(){
}
public executeRead(){
}
public executeUpdate(){
}
public executeDelete(){
}
}
The flow will be like this:
aObject ---> put it into DBCmd ----> put the DBCmd in DBManager ---> execute
But the problems happen when I get some objects related to other tables, for example...a customer have a purchase record, and which purchase record have many items....
So, what do I do in my read method? should I read all the records related to the customer?? Do I need to loop all the items inside the purchase record too?
If yes, when I doing read customer, I need to query 3 tables, but some that may not need to see.....it waste the resource...
And I come up with another solution, I make a new set of DBCmd, that allow me to get the related DB items, for example:
class getReleatedPurchaseRecordDBCmd{
public _constructor($aCustomerObject){
}
//.... ....
}
But in this "solution", I got some problems, is I loss the relationship in the object customer...yes, I can read back all the records, get the customer object basically don't know any things about the purchase record....
Some may ask me to do something like this:
class customer{
//skip other methods...
public getPurchaseRecords(){
//query the db
}
}
It works, but I don't want the object structure have some strong relationship between the db....That's why I come up with the DBCmd stuff...
So, everything seems to be very coupling, how can solve it? Thank you.
for stuff like this i tend to get the count of sub objects with the initial query usually involving sql COUNT and JOIN, then have a seperate getSubObjects command that can be called if needed later. So for example:
$datamodel->getCustomer($id);//or some such method
returns
class Customer{
$id = 4;
$recordCount = 5;
$records = null;
}
I can then use the count for any display stuff as needed, and if i need the records populated call:
$customer->records = $datamodel->getCustomerRecords($customer->id);

Categories