Using HasMany relationship when Model and table have different names - laravel

Like the title says, my Field model and application_fields table have different names. I already set the table name on the Field model to the correct table name. A set the I have another model, Application, and its controller, ApplicationController. In the index method of the ApplicationController, I am trying to retrieve the application and its fields. The Field model has a belongsTo relationship to Application, and the Application model has a hasMany relationship to Field.
The code seems to work, except it pulls the values for the rows from the applications table. So if the applications table has the following rows:
1 'AppOne' 'An App'
2 'AppTwo' 'Another App'
3 'AppThree' 'A third App'
AppOne, AppTwo, and AppThree are returned as the values for the fields.
Can someone give me some advice?
Here is the code for them( I only copied what was related to the issue, not the entire file):
Application model:
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'application_fields';
public function fields()
{
return $this->hasMany('App\Field');
}
Field model:
public function application()
{
return $this->belongsTo('App\Application');
}
ApplicationController:
/**
* Display the dashboard for a single application.
*
* #param String $id
* #return View
*/
public function show( $id )
{
$app = Application::where('id', $id)->first();
$fields = $app::with('application_fields')->get();
return view('applications.show', [
'application' => $app,
'fields' => $fields,
]);
}
View( I stripped out the HTML):
{{ $application->name }}
{{ $application->description }}
#foreach($fields as $field)
{{ $field->name}}
#endforeach
I would appreciate any advice on what I am doing wrong. Thanks

your show method is wrong, eager loading does not work that way :)
public function show( $id ) {
$app = Application::with('fields')->find($id);
$fields = $app->fields;
return view('applications.show', [
'application' => $app,
'fields' => $fields,
]);
}
you can also use model Binding if your route is like that : applications/{app}
public function show(Application $app) {
$fields = $app->fields;
return view('applications.show', [
'application' => $app,
'fields' => $fields,
]);
}

Related

Laravel Scout map in toSearchableArray relationship fields

Is it possible to map in toSearchableArray relationship fields as well. What I mean, having User model I try to search in related model fields as well like;
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
return [
'name' => $this->name,
'plz' => $this->account->zip,
];
}
/**
* #return HasOne
*/
public function account(): HasOne
{
return $this->hasOne(Member::class);
}
In controller searching after results
if($request->has('s')) {
$founds = User::search($request->get('s'))->get();
}
will throw Attempt to read property "zip" on null
I do not really find any infos in documentation related to this question
I do have two ways to do it of which I consider one of them as crude.
Method 1:
Here's an example implementation where you're searching all of one model and then a relationship (accounts)
public function toSearchableArray()
{
$array = $this->toArray();
$array = $this->transform($array);
$array['country'] = $this->countries->map(function ($data) {
return $data['name'] ?? '';
})->toArray();
return $array;
}
Method 2:
public function toSearchableArray()
{
$array = $this->toArray();
// Customize array...
$array = [
'user_name' => $this->user_name,
'country' => $this->getCountryNameById($this->country_id),
...
];
return $array;
}
where relationship is defined in helpers or you can make a separate trait and import it in model with method.
Here relationship for country is defined within the model as
//Get Country name By id
function getCountryNameById($id) {
$country = \App\Country::select('name')->find($id);
return $country->name ?? '';
}

Laravel Nova Actions BelongsTo field not working

I have this simple action:
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
$model->update([
'user_id' => $fields->user
]);
}
}
/**
* Get the fields available on the action.
*
* #return array
*/
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
At first, it seems fine, but when I select User from BelongsTo relation and try to save exception is throwing:
Argument 1 passed to Laravel\Nova\Fields\BelongsTo::getRelationForeignKeyName() must be an instance of Illuminate\Database\Eloquent\Relations\Relation, instance of Illuminate\Support\Fluent given, called in /Users/rd/Sites/bns-crm/vendor/laravel/nova/src/Fields/BelongsTo.php on line 212
Yes i know i'm late but - here's a solution for this:
Use a Select-Field instead of BelongsTo and Pluck your options to build Key-Value pairs:
public function fields()
{
return [
Select::make('debitor')->options(\App\Models\Debitor::pluck('Name', 'id'))
];
}
Then in the handle you should geht the ids in $fields:
public function handle(ActionFields $fields, Collection $models) {
Log::info($fields);
}
Maybe I'm late, but, for the ones like me wanting to use the BelongsTo searchable field because the model they want to search in contains too much records to pack them in a normal Select field here is the solution I found:
Create a class in App\Nova\Fields with this code:
<?php
namespace App\Nova\Fields;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Http\Requests\NovaRequest;
class BelongsToForActions extends BelongsTo
{
public function fillForAction(NovaRequest $request, $model)
{
$attribute = $this->attribute;
if ($request->exists($attribute)) {
$value = $request[ $attribute ];
$model->{$attribute} = $this->isNullValue($value) ? null : $value;
}
}
}
Then use it like you would use a normal BelongsTo field. Just remember to fill the 3 arguments on the make, so, for example:
BelongsToForActions::make('User', 'relation', \App\Nova\User::class)->searchable()
Remember that 'relation' must exist.
Check your namespaces. Did you imported right class? User class must be resource class
public function fields()
{
return [
BelongsTo::make('User', 'user', User::class),
];
}
I actually fixed this by mocking the key value pair used in this relationship.
First I build an array with the ID column as key and the name column as value.
$clients = Client::all()
->keyBy('id')
->map(fn($client): string => $client['name'])
->toArray();
Then I use the Select nova field to display it.
Select::make('Klant', 'client')
->searchable()
->options($clients)
->rules('required'),

Get specific columns having with() function in laravel

Category Model definition is:
class Category extends Model
{
public $implement = ['#RainLab.Translate.Behaviors.TranslatableModel'];
public $translatable = ['name'];
/**
* #var string The database table used by the model.
*/
public $table = 'shop_category';
/**
* #var array Validation rules
*/
public $rules = [
];
public $attachOne = [
'icon' => 'System\Models\File'
];
public $hasMany =[
'shops' => ['ItScholarBd\Api\Models\Shop', 'key' => 'shop_category_id']
];
}
I want to get category with shop. It works fine if I retrieve all columns but if I try to retrieve specific column it is not working. I want to get only the following columns:
id, name of each table. I also apply a condition where Shop.status = 1. I am trying as follows:
$this['categoryWithShop'] = Category::select('id,name')->with(array('shops'=>function($query){
$query->where('status','=',0);
$query->select('id','name');
}))->get();
Any idea?
Try this,
$this['categoryWithShop'] = Category::select('id,name')->with(array('shops'=>function($query){
$query->where('status','=',0);
$query->select('id','name', 'status');
}))->get();
In select query you need to pass that all columns which is needed in query.
try this chaining method and select('id,name') this is wrong you need to fix this as select('id','name')
ref link https://laravel.com/docs/8.x/queries#selects
$this['categoryWithShop'] = Category::select('id','name')->with(['shops' => function ($query) {
$query->select('id', 'name')->where('status', '=', 0);
}])->get();

How to merge 2 objects in Laravel and Vue.Js

Im try to create a simple CRUD App in Laravel + Vue.JS (Vuex also)
And I have small problem
What is the problem actually
I have a table 'categories'
Structure of this table u can see on screenshot
And i have created 2 test rows
u can see on screenshot
How can I insert a title instead of a value parent_id
If parent_id has value like someone ids
Im try v-for in Vue Componetn and v-if
But i dont have any results
This is my code:
<tr v-for="(category, $index) in categories" :key="category.id">
<td>{{$index + 1}}</td>
<td>{{category.title}}</td>
<td v-if="category.parent_id === null">Category</td>
<td v-else>SubCategory</td>
<td>{{category.created_at | moment("DD, MMMM, YYYY")}}</td>
<td></td>
</tr>
This is my data what im get from Controller
Anyway thanks for help
This is code of controller
public function index()
{
$result = ['success' => true];
$category = Category::paginate(15);
$result['category'] = $category->items();
$result['pagination']['currentPage'] = $category->currentPage();
$result['pagination']['total'] = $category->total();
$result['pagination']['perPage'] = $category->perPage();
return response()->json($result, 200);
}
And here is how I want it to be
I think render function adn array processing must be at the Vue Component
This is my Category model code
public function products()
{
return $this->hasMany('App/Products');
}
public function parent()
{
return $this->hasOne(Category::class,'id','parent_id');
}
Solution will be to define relation hasOne like
public function parent()
{
return $this->hasOne(Category::class,'id','parent_id');
}
And then you can define resource, where you can claim parent's title using defined relation like below
$this->parent->title
Create you resource using command
php artisan make:resource CategoryResource
Then in your controller you should use resource
use App\Http\Resources\CategoryResource;
and within controller's action provide response like
$categories = Category::paginate(15);
return CategoryResource::collection($results); // instead of response()->json($categories , 200);
And example how your resource should look like
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class CategoryResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'parent_id' => $this->parent_id,
'parent_title' => $this->parent->title,
...
];
}
}
Info about resources you can find https://laravel.com/docs/5.8/eloquent-resources

Indirect modification of overloaded on Laravel - Relationship

I use Laravel 5 and i have 2 tables:
"Link" Table with 7 columns, one of them is "image_id" column.
"Image" Table, in this table i have two column, an "id" and "url".
I want to update my Link database and also it's related image url with single update method.
I have tried this code:
public function update(Request $request, $id)
{
// Validate the data
$link = Link::find($id);
$this->validate($request, array(
'title' => 'required|max:255',
'link' => 'nullable',
'description' => 'required'
));
$link = Link::find($id);
$link->title = $request->input('title');
$link->link = $request->input('link');
$link->description = $request->input('description');
$link->linkImage->url = 'testurl';
$link->save();
Session::flash('success', "Le lien à été correctement mis à jour.");
return redirect()->route('links.index');
}
This is my Link class:
<?php
namespace App\Models\Services;
use Illuminate\Database\Eloquent\Model;
class Link extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'service_rubrique_link';
protected $fillable = ['title','description','ordre','image_id'];
public function linkImage()
{
return $this->belongsTo('App\Models\Storage\Imageup');
}
}
And my Image Class:
<?php
namespace App\Models\Storage;
use Illuminate\Database\Eloquent\Model;
class Imageup extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'image';
protected $fillable = ['id','url'];
public function servlinks()
{
return $this->hasMany('App\Models\Services\Link');
}
}
When i want to save i have this error:
Indirect modification of overloaded property
App\Models\Services\Link::$linkImage has no effect
Do you have any idea of what is wrong with my code ?
Thank you.
As the message says the property is a lazy loaded sub model.
Updating does not makevsense... you should use it as method (query builder) linkimage() and then use for example ->update(['propertyname' => 'value'])
See https://laravel.com/docs/7.x/eloquent-relationships#inserting-and-updating-related-models

Resources