I'm struggling to get my head around a Laravel hasManyThrough and League/Fractal Transformers.
I have three tables:
Countries:
-id (int)
-other fields ...
Cities:
-id (int)
-country_id (int)
-other fields ...
Users
-id (int)
-city_id (string)
-some other ...
I'm trying to access Users relation from Country via Cities table relation, which is working using following Eloquent query:
$countryUsers = Country::with('users')->where('id', $id)->get();
But when I am trying to user $fractal to transform this relation, I'm getting Segmentation fault (core dumped) error.
In Country Controller I have:
class CountryController extends ApiController
{
protected $manager;
function __construct(Manager $manager) {
$this->manager = $manager;
}
public function show(CountryTransformer $countryTransformer, $id) {
$country = Country::find($id);
return $this->respondItem($country, $countryTransformer);
}
public function respondItem($item, $transformer)
{
$this->manager->setSerializer(new CustomArraySerializer());
$resource = new Item($item, $transformer);
$data = $this->manager->createData($resource)->toArray();
return $this->respond($data);
}
In my country model I have:
public function users() {
return $this->hasManyThrough('App\Models\User', 'App\Models\City');
}
public function cities() {
return $this->hasMany('App\Model\City');
}
City Model:
public function country() {
return $this->belongsTo('App\Models\Country');
}
User Model:
public function city()
{
return $this->belongsTo('App\Models\City');
}
and Fractal country transformer:
<?php namespace App\Transformers;
use App\Models\Country;
use League\Fractal\TransformerAbstract;
class CountryTransformer extends TransformerAbstract {
protected $defaultIncludes = [
'users'
];
public function transform(Country $country)
{
return [
'id' => $country->id,
'name' => $country->name,
'code' => $country->code,
'time_zone' => $country->time_zone,
'active' => $country->active,
'status' => $country->status,
'params' => $country->params
];
}
public function includeUsers(Country $country)
{
$users = $country->users;
if ($users) {
return $this->collection($users, new UserTransformer());
}
}
}
If anyone can point me in the right direction I'd really appreciate it. Thanks.
Related
I'm trying to figure out how to create all the data based on this relationship testing in Laravel.
Company Model
class Company
{
public function stores()
{
return $this->hasMany(Store::class, 'company_id');
}
public function employers()
{
return $this->belongsToMany(User::class, 'employers',
'company_id', 'user_id');
}
}
Store Model
class Store
{
public function company()
{
return $this->belongsTo(Company::class, 'company_id');
}
public function employers()
{
return $this->belongsToMany(User::class, 'employers',
'store_id', 'user_id');
}
}
User Model
class User
{
public function company()
{
return $this->belongsToMany(Company::class, 'employers',
'user_id', 'company_id');
}
public function store()
{
return $this->belongsToMany(Store::class, 'employers',
'user_id', 'store_id');
}
}
$company = Company::factory()->hasStores(
Store::factory()->hasEmployers(User::factory())
)->create();
dd($company) // App\Models\Company {#2470... Ok!
$store = $company->store()->first();
dd($store) // App\Models\Store {#2479... Ok!
$user = $store->employers()->first();
dd($user) // null (T-T)
Background: this is an application that allows a proprietor to own several companies. For that reason, I got many relationships, and even so, employees sometimes can only belong to a single company or store.
Try:
$store = $company->store->first();
dump($store);
$user = $store->employers->first();
dump($user);
Try something like this with DB Facade
private $employ;
public function setUp(): void
{
$this->employ = Employ::factory()->create([
'id' => 14,
'name' => 'Name Employ'
]);
}
public function test_pivote_table()
{
$user = User::factory()->create([
'name' => 'User test'
);
//here :)
DB::table('name_pivote_table')->insert([
'user_id' => $user->id,
'employ_id' => $this->employ->id
]);
}
Credits to Fguzman :)
I am building a small application in Laravel where I got stuck with the sum of inner relational data,
I have a model Company which has Many relation associatedProjects and associatedProjects belongs to relation project and project hasOne technicalDescription.
Company Model:
class Company extends Model {
public function roles()
{
return $this->belongsToMany('Noetic\Plugins\Conxn\Models\Variables\Company\Role', 'company_role_relation', 'company_id', 'role_id')->withTimestamps();
}
public function specialisations()
{
return $this->belongsToMany('Noetic\Plugins\Conxn\Models\Variables\Company\Role', 'company_specialisation_relation', 'company_id', 'specialisation_id')->withTimestamps();
}
public function associatedProjects()
{
return $this->hasMany('Noetic\Plugins\Conxn\Models\Project\AssociateCompany','company_id','id');
}
}
AssociateCompany Model:
class AssociateCompany extends Model {
protected $table = 'project_associate_company';
protected $fillable = [
'project_id', 'company_role_id', 'company_specialisation_id', 'company_id', 'link', 'file_name'
];
public function project()
{
return $this->belongsTo('Noetic\Plugins\Conxn\Models\Project','project_id','id');
}
public function company()
{
return $this->belongsTo('Noetic\Plugins\Conxn\Models\Company','company_id','id');
}
public function companyRole()
{
return $this->belongsTo('Noetic\Plugins\Conxn\Models\Variables\Company\Role',
'company_role_id','id');
}
public function specialisation()
{
return $this->belongsTo('Noetic\Plugins\Conxn\Models\Variables\Company\Role',
'company_specialisation_id','id');
}
}
Project Model
class Project extends Model {
protected $fillable = [
'user_id','koshy_id', 'name', 'slug', 'owner_spv', 'spv_link', 'latitude', 'longitude',
'landmark', 'city', 'district', 'state', 'pin_code', 'region_id', 'country', 'building_use',
'sector', 'conxn_id', 'parent_project_id', 'website', 'project_logo', 'tracked', 'verified',
'code_link', 'status', 'active', 'premium','area'
];
public function technicalDescription()
{
return $this->hasOne('Noetic\Plugins\Conxn\Models\Project\TechnicalDescription','project_id','id');
}
public function associateCompany()
{
return $this->hasMany('Noetic\Plugins\Conxn\Models\Project\AssociateCompany','project_id','id');
}
}
Now this technicalDescription has fields construction_cost, now I want to first count total number of associatedProject and fetch sum of all the project's construction_cost which is in technicalDescription, some what I have done this code:
$company = Company:: where( 'status', 'saved')
->withCount( 'associatedProjects' )
->with('associatedProjects.project.technicalDescription')
->get()
->transform(function ($value) {
$value['project_value'] = $value['associatedProjects']->flatten(2)
->pluck('project.technicalDescription')->sum('construction_cost');
return $value;
})
->sortByDesc('project_value')
->forpage( $request->page , 10 );
$next = $request->page+1 ;
$previous =$request->page-1 ? abs($request->page-1):1 ;
I am unable to use paginate over here as laravel collection doesn't have such method, moreover the query logic also doesn't appear accurate.
Any suggestions are welcome. Thanks
You can use a BelongsToMany relationship to get the technicalDescriptions directly:
class Company extends Model {
public function technicalDescriptions() {
return $this->belongsToMany(
'Noetic\Plugins\Conxn\Models\Project\TechnicalDescription',
'project_associate_company',
'company_id',
'project_id',
null,
'project_id'
);
}
}
$company = Company::where('status', 'saved')
->withCount(['technicalDescriptions as project_value' => function($query) {
$query->select(DB::raw('sum(construction_cost)'));
}])
->orderByDesc('project_value')
->paginate();
My laravel eloquent like this :
$query = ItemDetail::selectRaw('a.item_number, sum(abs(a.quantity)) as "total_quantity"')
->from('item_detail as a')
->join('items as b', 'b.id', '=', 'a.item_number');
if(isset($param['vendor'])) {
$query = $query->where('b.vendor_id', '=', $param['vendor']);
}
$query = $query->groupBy('a.item_number')
->paginate($paged);
return $query;
If the query executed, there exist error like this :
Relation 'a' is not instance of HasOne or BelongsTo.
How can I solve this problem?
Update
My item model like this :
class Item extends Model
{
...
protected $fillable = [
'name',
'vendor_id'
];
public function item_details()
{
return $this->hasMany(ItemDetail::class, 'id', 'item_number');
}
}
My item detail model like this :
class ItemDetail extends Model
{
....
protected $fillable = [
'item_number',
'name',
'posting_date'
];
public function item()
{
return $this->belongsTo(Item::class, 'id', 'item_number');
}
}
According to your few details, please confirm if this is what you want:
Item Model:
class Item extends Model
{
...
protected $fillable = [
'name',
'vendor_id'
];
public function item_details()
{
return $this->hasMany(ItemDetail::class, 'item_number', 'id');
}
}
ItemDetail Model:
class ItemDetail extends Model
{
....
protected $fillable = [
'item_number',
'name',
'posting_date'
];
public function item()
{
return $this->belongsTo(Item::class, 'item_number', 'id');
}
}
Controller Method:
$itemDetails = ItemDetail::whereHas('item', function($q) use ($param) {
if (isset($param['vendor']){
$q->where('vendor_id', '=', $param['vendor']);
}
})
->selectRaw('item.id', 'item_detail.item_number', 'sum(abs(a.quantity)) as total_quantity')
->groupBy('item_number')->paginate($paged);
View file:
#foreach($itemDetails as $itemDetail)
#dump($itemDetail->item_number)
#dump($itemDetail->total_quantity)
#endforeach
It makes more sense to access the main model you want and include in it, the relationships you need to include.
Here's a potential different solution:
$query = Item::with(['item_details' => function ($query) {
$query->groupBy('item_number')->selectRaw("item_number, sum(abs(a.quantity)) as 'total_quantity'");
}]);
if (isset($param['vendor'])) {
$query->where('vendor_id', $param['vendor']);
}
$itemsPage = $query->paginate($paged);
return $pages;
You can access the paged data if you do:
foreach ($itemsPage as $item) {
// $item is an instance of Item
// $item->item_details->total_quantity should have the sum of the item details
}
I can only guess because of too few information: your model is missing something like this:
public function a()
{
return $this->belongsTo(RelatedModel::class);
}
I want to insert a Post record with multiple tags so here's my code in Post#store:
$post = Post::create(array(
'title' => $request->title,
'body' => $request->body,
'user_id' => Auth::id(),
));
if($post && $request->tags)
{
$tagNames = explode(',', $request->tags);
$tagIds = [];
foreach($tagNames as $tagName)
{
$tag = Tag::firstOrCreate(['name'=>$tagName]);
if($tag)
{
$tagIds[] = $tag->id;
}
}
$post->tags()->attach($tagIds);
}
but it give me an errors "Call to a member function attach() on null". when i'm checked in mysql the tags is already in there but i can't find any entry on my post_tag tables. here's my post model:
class Post extends Model
{
protected $fillable = ['user_id','title','slug','body','tags','category_id','featured'];
public function category()
{
return $this->belongsTo('App\Category');
}
public function tags()
{
$this->hasMany('App\Tag');
}
}
You need to return the call the hasMany in your Post model.
public function tags()
{
return $this->hasMany('App\Tag');
}
Update
You should use belongsToMany not hasMany.
Model
class Profile extends Eloquent {
public function user(){ return $this->belongsTo('User'); }
public function url(){
return URL::Route('profile', array('city_slug' => $this->city_slug, 'slug'=> $this->slug));
}
}
Controller
class ProfileController extends BaseController {
public function show($user_id)
{
$profile = Profile::with('user')->where('user_id', $user_id);
return Response::json($profile, 200);
}
}
I would like the response to include the generated URL from the Profile method url(){}
{
"id" : 1,
"url" : /profile/{city_slug}/{slug},
....
}
Thanks in advance.
I would go at this the other way.
In your Users model...
public function profile()
{
return $this->belongsTo('Profiles','profile_id');
}
Then you can use $profile = User::find(user_id)->profile.
$data = array(
'id' => $profile->id,
'url' => "/profile/".$profile->city_slug."/".profile->slug
);
And then finally
return Repsonse::json($data,200);