Eloquent relation condition 2 columns foreign key - mysql

I have case like this
table transactions
|id|account_from_id|account_to_id|value|
table account
|id|name|type|
I would like to use eloquent relation from table account to transaction which is it need additional condition, account as from, or account as to, but in one function only relation
this is my code
public function transactions(){
$result = $this->hasOne('App\Transactions','id', 'account_from_id');
if(!is_null($result)){
return $result;
}
return $this->hasOne('App\Transactions','id' 'account_to_id');
}
But it not work as I expected, when account as to, it will return null

The model column has a relation must be define on reference table and column
make each column has a relation on your model
public function account_from(){
return $this->hasOne('App\Transactions','id', 'account_from_id');
}
public function account_to(){
$this->hasOne('App\Transactions','id', 'account_to_id');
}
define the account transaction by the new transaction attribute
public function getTransactionAttribute(){
if(!empty($this->account_from_id)){
return $this->account_from
}else{
return $this->account_to
}
}

Related

Laravel: resolving the n + 1 problem that has raw query or custom function

I am working on a Laravel application. I am having a bit of a problem with querying the data.
This is my database schema.
I have a users table with the following fields.
users
- id
- name
- email
- password
Then I have the transactions table with the following fields.
transactions
- id
- amount
- user_id
- type
- created_at
As you can see transactions table has the user_id as a foreign key to the users table.
This is the User model.
class User extends Model
{
public function transactions()
{
return $this->hasMany(Transaction::class, 'user_id');
}
}
This is the Transaction model
class Transaction extends Model
{
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
Now, I am trying to get a user's transactions with additional column and conditional checking. Normally, if I wanted to get a user's transactions, I would do something like this.
$user->transactions()->get()
But I want to get an extra column for each row, for each row, I would like to get the sum of the amount of transactions until the created_at of transactions of the current row.
If I have to write a function for the single record, it would be something like this.
function totalAmountUntil($user, $date)
{
return $user->transactions->where('transactions.created_at', '<=', $date)->sum('transactions.amount');
}
I need to apply that to each row in the query based on the created_at of the transactions also resolving the n+1 problem. How can I do that?
You can use withCount for this :
User::withCount([
'transactions as transactions_amount_sum' => function($query) use ($date){
$query->where('created_at', '<=', $date)
->select(DB::raw('SUM(amount)'));
}
])->get();

Time column value change without touching it

I have a "Trip" model that has "time" column that represents the trip time (in addition to the created_at and updated_at) columns. There's a hasOne relation between the "Trip" model and a "Bus" model (every bus can be associated to more than 1 trip).
I want to set the "bus_id" column in the "Trips" table to null on deleting the corresponding bus.
When I do so, something very strange happens, the "time" column in the "Trips" table changes to the current time (similar to updated_at).
Note:
This happens only in this case; any update to the "Trip" table doesn't change the column.
When I inspect the code with dd(), I find the "time" column unchanged, but then the value is changed in the database.
Trip Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Trip extends Model
{
//
protected $fillable = ['time', 'bus_id'];
public function getStationsStringified(){
$stations = $this->stations()->orderBy('order', 'asc')->pluck('name')->toArray();
return implode(",", $stations);
}
public function bus(){
return $this->belongsTo(Bus::class);
}
public function tickets(){
return $this->hasMany(Ticket::class);
}
public function stations(){
return $this->belongsToMany(Station::class)->withPivot("order");
}
}
Bus Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Bus extends Model
{
//
protected $fillable = ['license_num'];
public function trips(){
return $this->hasMany(Trip::class);
}
}
Destroy function in BusController:
public function destroy($id){
$bus = Bus::find($id);
foreach ($bus->trips as $trip){
if($trip->time < Carbon::now()->startOfDay()){
$trip->bus()->dissociate();
$trip->save();
}else{
return new Response("There a trip ".$trip->getStationsStringified()." associated with this bus, please change its bus before deletion", Response::HTTP_CONFLICT);
}
}
$bus->delete();
return new Response("Deleted", Response::HTTP_OK);
}
The problem was that I used timestamp() in the migrationn file defining the trips table, which adds automatically extra function on the time column set to current time on update.
The reason why the problem doesn't happen on any other updates is that I was setting a value to the time column.
The solution is to use datetime() in the migration instead of timestamp().

Laravel migration MySQL foreign key

I have two tables:
Users (id, name)
Events (id, name, date, user_created_id, user_updated_id (etc...)
How can I make a relationship between the two ids of the Events table with the user id?
user_created_id -> users.id
user_updated_id -> users.id
UPDATE
$table->integer('user_created_id')->unsigned();
$table->integer('user_updated_id')->nullable();
$table->foreign('user_created_id')->references('id')->on('users');
$table->foreign('user_updated_id')->references('id')->on('users');
If your question is how to make a foreign key for those two columns, then here:
$table->unsignedInteger('user_created_id');
$table->unsignedInteger('user_updated_id');
$table->foreign('user_created_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('user_updated_id')->references('id')->on('users')->onDelete('cascade');
If your id column on the users table is bigIncrements then replace the unsignedInteger with unsignedBigInteger
In the Event model, you can define two relationships:
public function user_created(){
return $this->belongsTo('App\User', 'user_created_id');
}
public function user_updated(){
return $this->belongsTo('App\User', 'user_updated_id');
}
In your User model, you can also define the reverse relationships:
public function created_events(){
return $this->hasMany('App\Event', 'user_created_id');
}
public function updated_events(){
return $this->hasMany('App\Event', 'user_updated_id');
}
With these relationshops, you can call $event->user_created to retrieve the App\User linked to the user_created_id (same for the updated version).
With a App\User instance, you can call $user->created_events to get a collection of App\Event (again, it's the same with $user->updated_events).

Joining Two Tables to a Reference Table Laravel

I Have three tables
#1 Table timeline which is my reference table with an Auto incremented ID which is stored in column id
#2 timeline_videos
#3 timeline_else
What happens is on post if a video is uploaded with the post
it will go into Table #2 ,anything else goes into Table #3.
#2-3 have the Auto Increment Id from the Table timeline stored in a column pid
On query of The Timeline I need to join both tables data using id=pid
so I can use the rest of the Relational Data with the post.
I have done a bit of research and can't seem to find a method for doing so.
So far the code I have
Controller
$groupposts = timeline::where([
['owner','=',$owner],['id','<',$lastid],
])
->join('timeline_videos','timeline.id','=','timeline_videos.pid')
//->join('timeline_else','timeline.id','=','timeline_else.pid')
->orderBy('id','desc')
->limit(5)
->get();
This works with no errors with the second Join commented out but I need to also grab the timeline_else data .
Update --
I have now decided to use Eloquent Relationships to join the tables,
my question now is what type of relationship do I have between the
tables?
I realize it basically needs to be able to switch between two tables to
grab data based on the fact that timeline_videos and timeline_else will not be "JOIN" but separated by type .
The tables need to Join with table #1 timeline based on a column I now have named type for clarifying where to look and matching/joining using the id = pid
You can use relationships.
it sounds like timelines has many videos and has many video failures
https://laravel.com/docs/5.5/eloquent-relationships#one-to-many
you would have a model for each table and set up the relationships
timelines model:
public function videos()
{
return $this-> hasMany('App\Videos');
}
public function videoFailures()
{
return $this-> hasMany('App\videoFailures');
}
videos model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
videos failures model:
public function timeline()
{
return $this->belongsTo('App\Timelines');
}
You can then go:
$timeLine = Timmeline::find($id);
to find videos of the time lines you would do:
$videos = $timeLine->videos();
to find else:
$videoElse = $timeLine-> videoFailures();
By using some of what Parker Dell supplied and a bit more trial and error
My Models Looks like
timeline
class timeline extends Model
{
protected $table ='timeline';
public $timestamps = false;
public function videos()
{
return $this->hasMany('App\timeline_videos','pid','id');
}
public function else()
{
return $this->hasMany('App\timeline_ect','pid','id');
}
}
timeline_ect.php ,I changed the name of the table
class timeline_ect extends Model
{
protected $table='timeline_ect';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\Models\timeline','pid','id');
}
}
timeline_videos
class timeline_videos extends Model
{
protected $table='timeline_videos';
public $timestamps = false;
public function timeline()
{
return $this->belongsTo('App\timeline','id','pid');
}
}
Then Lastly my Controller
$timeline = timeline::with('videos','else')
->orderBy('id','desc')
->limit(5)
->get();
So far no Problem query is correct.

Relationships between tables in laravel using backpack package

I am using backpack CRUD package to create my website project in laravel 5.2
I want to establish a relationship between two tables. First table is called customer and second table is called transaction. Each customer has many transaction(1:N relationship).
Customer table record:
ID Name
123456 xyz
Transaction table record:
ID CustomerID
101010 123456
I know that I have to specify the relation in the customer model. But, how can I display the result of the relationship in CRUD ?
You should have relationships on both the Transaction and the Customer models, so you can do $customer->transactions and $transaction->customer:
class Customer extends Model
{
/**
* Get the comments for the blog post.
*/
public function transactions()
{
return $this->hasMany('App\Transactions', 'CustomerID', 'ID');
}
}
and
class Transaction extends Model
{
/**
* Get the comments for the blog post.
*/
public function customer()
{
return $this->belongsTo('App\Customer', 'CustomerID', 'ID');
}
}
Spend some time in the Eloquent Relationships Documentation. It's really important to understand them if you want to be a Laravel developer.
In order to display the relationship in the CRUD, you can then use Backpack's select column type to display it in the table view and select or select2 field types to display it in the add/edit views. Read the CRUD Example Entity to better understand how that works.
First of all when you are creating migrations for both tables, table which contain Foreign Key (FK) must have field like this:
public function up(){
$table->increments('id');
$table->integer('customerID')->unsigned();
}
After that you are need to call next command into console
php artisan migrate
Next is going next commands:
php arisan backpack:crud customers
php arisan backpack:crud transactions
After that you need to define functions in models which returns values from other tables. Customer models need to have next function
public function transactions(){
return $this->hasMany('Transaction');
}
Transaction model must have next function
public function customer() {
return $this->belongsTo('Customer');
}
Next you must add CRUD field in Customer controller to display
transactions in select box.
$this->crud->addField([
'label' => 'Transactions', // Label for HTML form field
'type' => 'select2', // HTML element which displaying transactions
'name' => 'customerID', // Table column which is FK for Customer table
'entity'=> 'customer', // Function (method) in Customer model which return transactions
'attribute' => 'ID', // Column which user see in select box
'model' => 'Transaction' // Model which contain FK
]);
Hope this helps :)
After you built onetomany relationship with transaction, you can get the results.
$customer=Customer::where(['id'=>'123456'])->with('transaction')
->first();
print_r($customer->Name); // gives the customer name
foreach($customer->transaction as $cid)
{
print_r($cid->CustomerID); // gives the customer id
}
Laravel Relationships Documentation is always helpful. Go through it.