Grouping rows which has same value in same column - laravel

I am having a hard to achieve this grouping issue. I have a master table that contains names in name column. And there is another table connected to master table by belongsTo relationship and contains plans for each names.
The problem is, in master table some names repeating a couple of times. The structure is like below.
| name |
--------------------
| Mark
| Mark
| Jack
| Alistair
| Oliver
| Jack
I want to group rows if has same value(name). At the same time try to avoid to lose connected tables data. Because each names has own plan in another table.
Master::select('name')
->groupBy('name')
->get();
If I do something like above I am grouping names. But this time of course I am losing the connected table's data.
Is there anyway to achieve, what I wanted here.
Here is the table structure and Models for the connection between tables.
master table
Schema::create('masters', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
plan table
Schema::create('plans', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('master_id');
$table->string('website');
$table->string('url');
$table->string('plan_name');
$table->timestamps();
});
master model
public $with = ["plan];
public function plan()
{
return $this->hasMany("App\Plan");
}
plan model
public function master()
{
return $this->belongsTo("App\Master");
}

Group by doesnot allows us to select all the column so you can join and use order by.
DB::table('masters as m')->select(m.*,p.*)
->join('plans as p', 'p.master_id', '=','m.id')
->orderBy('m.name', 'asc')
->orderBy('p.id', 'asc')

Related

update content of stored column in laravel controller

I have a table with 3 columns:
firstname
lastname
fullname
in migration:
Schema::create('owners', function (Blueprint $table) {
$table->id();
$table->string('firstname',20);
$table->string('lastname', 20);
$table->string('fullname')->storedAs('CONCAT(firstname,lastname)');
$table->timestamps();
});
the problem is that i want to change the concatenation order in the controller i tried to use db statement but it doesn't work
-in the controller:
$owners= Owner::findOrFail($id);
$owners->update([
'firstname'=>$request['firstname'],
'lastname' =>$request['lastname'],
]);
DB::statement('UPDATE owners SET fullname AS CONCAT(lastname,firstname) STORED WHERE ID=1 ');
I don't want to just use a simple concatenation because the user can change the firstname or the lastname and the order that's why I used storedAs()
any ideas please?
The storedAs method in the migration creates a generated column in mysql. The value is automatically generated from the column values of the firstname and the lastname. There's no way you can change this via an UPDATE statement. You'd have to use an ALTER TABLE statement, which would be horrifically bad practice.
If I were you, I'd keep full name display as a model method so you could access it by using $owner->fullNameFirstLast() or $owner->fullNameLastFirst()
What you should do is create a new migration in order to change the column, the code would be something like this:
Schema::table('owners', function (Blueprint $table) {
$table->string('fullname')->storedAs('CONCAT(lastname,firstname)');
});
This way the column will be changed on a database level, and no need for the controller query you have added
Simply try this
1- update your migration to
Schema::create('owners', function (Blueprint $table) {
$table->id();
$table->string('firstname',20);
$table->string('lastname', 20);
$table->string('fullname', 56);
$table->timestamps();
});
2- in your controller
$owners= Owner::findOrFail($id);
$first_name = $request->firstname ?? $owners->firstname;
$last_name = $request->lastname ?? $owners->lastname;
$full_name = $first_name.' '.$last_name;
$owners->update([
'firstname'=>$first_name,
'lastname' =>$last_name,
'fullname' =>$full_name,
]);
You can also write it this way
DB::statement(DB::raw("UPDATE owners SET firstname = '".$first_name."', lastname = '".$last_name."', fullname = '".$full_name."' WHERE id = $id"));
And the same way for your Create function as well

How to get 1 row in pivot table?

In laravel 9 I have table pivot table defined :
Schema::create('article_vote', function (Blueprint $table) {
$table->id();
$table->foreignId('article_id')->references('id')->on('articles')->onUpdate('RESTRICT')->onDelete('CASCADE');
$table->foreignId('vote_id')->references('id')->on('votes')->onUpdate('RESTRICT')->onDelete('CASCADE');
...
});
and having in both models methods with belongsToMany
I can refer articles of a vote as :
$voteArticles = $vote->articles;
I got all related articles, but how can O get only 1 row if I have $articleId ?
Thanks!
That can be achieved in a number of ways by querying the relations:
$vote->articles()->where('id', $articleId)->first();
$vote->articles()->whereKey($articleId)->first();
$vote->articles()->find($articleId);
$vote->articles()->findOrFail($articleId);
Or in your example, you already have a collection of articles. We can also do this:
$vote->articles->find($articleId);
$vote->articles->findOrFail($articleId);

Laravel query where column value is greater than sum of related model column values

I have two tables transactions and transaction_allocations. One transaction can have many allocations.
Transaction Model
Schema::create('transactions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->foreignId('contact_id')->constrained();
$table->decimal('amount',19,4)->nullable();
});
Allocation Model
Schema::create('transaction_allocations', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamp('created_at');
$table->foreignId('transaction_id')->nullable()->constrained();
$table->foreignId('bill_id')->nullable()->references('id')->on('bills');
$table->decimal('amount',19,4)->nullable();
});
Relationship in Transaction Model
public function allocations(){
return $this->hasMany(TransactionAllocation::class);
}
I need to query all transactions where the transaction amount is greater than the sum of all allocated amounts for that transaction. (Basically finding transaction which has unallocated balances).
Transaction::where('contact_id',$id)->where('amount','>',sum of allocations)->get();
How do I achieve it?
I was able to create an accessor to do this calculation and find an unallocated amount. But seems like accessors cannot be used in where. I don't want to load all transactions and then filter it as it will be too much.
I want to query and get filtered lines directly. How do I do it?
I think Having Raw is better to compare two column.
Transaction::where('contact_id',$id)
->with('allocations')
->whereHas("allocations",function ($query){
$query->havingRaw('transactions.credit>sum(amount)');
$query->groupBy('transaction_id');
})->orDoesntHave("allocations")
->get();
if query fails then try to 'strict' => false in database.php for mysql connection

Eloquent using wrong key for some tables

Using: Laravel 5.5
I Constructing addresses using some address elements (like: district, area, zip etc) as dropdowns & also some user inputs.
I have 5 address element & one of their schema is:
Schema::create('address_districts', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('name');
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
This is for Districts, & I have another 3 exactly same like this called, address_thanas, address_areas, address_building_names & address_zips;
The only exception for the last one is that has code instead of name on other tables:
Schema::create('address_zips', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('code'); // Look other table has name here........
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
I store constructed addresses on the table called addresses
Schema::create('addresses', function (Blueprint $table) {
$table->increments('id');
$table->integer('district_id')->unsigned();
$table->integer('thana_id')->unsigned();
$table->integer('area_id')->unsigned();
$table->integer('zip_id')->unsigned();
$table->integer('building_name_id')->nullable()->unsigned();
$table->string('building');
$table->integer('floor');
$table->string('apt')->nullable();
$table->text('comment')->nullable();
$table->timestamps();
$table->foreign('district_id')->references('id')->on('address_districts');
$table->foreign('thana_id')->references('id')->on('address_thanas');
$table->foreign('area_id')->references('id')->on('address_areas');
$table->foreign('zip_id')->references('id')->on('address_zips');
$table->foreign('building_name_id')->references('id')->on('address_building_names');
});
In Address Model I've defined relationships like:
public function district() {
return $this->belongsTo(AddressDistrict::class, 'district_id');
}
public function thana() {
return $this->belongsTo(AddressThana::class, 'thana_id');
}
public function area() {
return $this->belongsTo(AddressArea::class, 'area_id');
}
public function building_name() {
return $this->belongsTo(AddressBuildingName::class, 'building_name_id');
}
public function zip() {
return $this->belongsTo(AddressZip::class, 'zip_id', 'id');
}
Then when I try to create a new address using Address::create($data)
I get error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'where clause' (SQL: select count(*) as aggregate from `address_zips` where `name` = 2)
Here we can see that it is comparing the key name instead of id
I noticed that this is not reporting that the data cannot be inserted or something like that, it fails to count related model & for that aborts insertion of data
Why is that?
But the strange thing is I can retrieve data by (inserted a row manually into db for testing to see if that can retrieve data)
$addresses = Address::orderByDesc('created_at')->get();
//loop as $address
$address->district->name
$address->zip->code
...
& this works perfect
When I am creating a new record I need that query look like:
select count(*) as aggregate from `address_zips` where `id` = 2
Any help will be highly appreciated.
Thanks for reading this long question.
The problem is in your $data array. Probably you copied a form with an input name, you need to rename it to code in order to save the values automatically.
Alternatively, you can set the input manually
$address = new Address();
$address->code = request('name');
$address->save();

How to make combination of two columns as a unique key?

I have this table :
public function up()
{
Schema::create('edition', function (Blueprint $table) {
$table->increments('id');
$table->integer('volume');
$table->text('cover')->nullable();
$table->enum('number', ['1', '2']);
$table->timestamps();
});
Schema::table('journal', function(Blueprint $table) {
$table->foreign('id_edition')->references('id')->on('edition')->onDelete('cascade')->onUpdate('cascade');
});
}
I wanted to make the combination of column Volume and Number as a unique key. For example, there is a data "Volume 1, Number 1" and when user insert the same combination it will throw an error message that the data already exist. Is there a way I can do this in Laravel ?
Just include the following snippet inside the up() method
$table->unique(['volume','number']);
By having this constraint set, if you insert 'Volume 1 , Number 1` once it'll be fine. However, if you try to do the same again, it'll throw an error.
Conditions:
You are using some good DBMS, such as MySQL and not SQLITE
You successfully migrated this change into the database.
You can also use uniquewith-validator package that allows you to validate a unique combination of fields like this
'field1' => 'required|unique_with:table_name,field2'
The above code will check if the combination of field1 and field2 is unique in the table with the given table_name.

Resources