Related
I am building an error tracking system using Laravel and MySQL so far I have 3 tables
fillers as f: Holds the fillers id, first, and last name columns:
id,= el.tech,
first_name,
last_name
error_type as et: Holds a table to describe the types of possible errors and assigns an id columns:
id,
name = el.error_id
error_logs as el: Holds all errors for all fillers columns:
id,
error_id, =et.name
tech, =f.id
public function countAllLogs()
{
$errorLog = DB::table('fillers AS f')
->select(
'f.id as id',
'f.first_name as first_name',
'f.last_name as last_name',
// 'f.location as location',
'et.name as error_type',
)
->selectRaw(
'count(el.error_id) as count',
)
->groupBy('f.id')
->groupBy('el.error_id')
->leftjoin('error_logs as el', 'f.id', '=', 'el.tech')
->leftjoin('error_types as et', 'et.id', '=', 'el.error_id')
->get();
return response($errorLog, 200);
}
using Postman Get function gives the following example
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"error_type": "ndc",
"count": 3
},
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"error_type": "jumper",
"count": 1
}
That is almost what I want but it separates out based on the "error_type." I have seen "pivot" but am unsure how to implement this with what I have.
The desired output would be
{
"id": 59,
"first_name": "Steve",
"last_name": "Martian",
"jumper": 1,
"ndc": 3
}
Thanks.
You can achieve this with the flatMap collection method. I'm assuming multiple ids will be present, so groupBy and map will also be used.
$errorLog = DB::table('fillers AS f')->...->get();
$json = $errorLog
->groupBy('id')
->map->flatMap(fn($item) => [
'id' => $item->id,
'first_name' => $item->first_name,
'last_name' => $item->last_name,
$item->error_type => $item->count
])
->values()
->toJson();
return response($json, 200);
Explanation
Here's the original collection. I've added a couple of entries to demonstrate different ids.
array:4 [
0 => {#4540
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
1 => {#4567
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
2 => {#4569
+"id": 57
+"first_name": "qwer"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
3 => {#4573
+"id": 58
+"first_name": "asdf"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
groupBy('id'): Groups the collection by id. This is important for the next step.
array:3 [
59 => Illuminate\Support\Collection {#4592
#items: array:2 [
0 => {#4540
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
1 => {#4567
+"id": 59
+"first_name": "Steve"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
}
57 => Illuminate\Support\Collection {#4557
#items: array:1 [
0 => {#4569
+"id": 57
+"first_name": "qwer"
+"last_name": "Martian"
+"error_type": "ndc"
+"count": 3
}
]
}
58 => Illuminate\Support\Collection {#4591
#items: array:1 [
0 => {#4573
+"id": 58
+"first_name": "asdf"
+"last_name": "Martian"
+"error_type": "jumper"
+"count": 1
}
]
}
]
flatMap(Closure): maps the collection and then collapses it. Same as map(Closure)->collapse().
map->flatMap(fn($item) => [...]): Shorthand for
map(function ($grouped) {
return $grouped->flatMap(function ($item) {
return [...];
});
});
This notation is possible thanks to high-order collection methods.
If the groupBy step was not done, then you'd end up with a single item, data completely mixed together.
Here's how the Collection looks like after map->flatMap(Closure):
array:3 [
59 => Illuminate\Support\Collection {#4590
#items: array:5 [
"id" => 59
"first_name" => "Steve"
"last_name" => "Martian"
"ndc" => 3
"jumper" => 1
]
}
57 => Illuminate\Support\Collection {#4570
#items: array:4 [
"id" => 57
"first_name" => "qwer"
"last_name" => "Martian"
"ndc" => 3
]
}
58 => Illuminate\Support\Collection {#4588
#items: array:4 [
"id" => 58
"first_name" => "asdf"
"last_name" => "Martian"
"jumper" => 1
]
}
]
values(): Discard the array keys. This is important because toJson() would produce a json object instead of a json array if it tried to conserve the keys.
You can see that for yourself with a simple experiment: Compare the results between
json_encode([ ['a' => 2, 'b' => 3] ]);
json_encode([1 => ['a' => 2, 'b' => 3] ]);
array:3 [
0 => Illuminate\Support\Collection {#4590
#items: array:5 [
"id" => 59
"first_name" => "Steve"
"last_name" => "Martian"
"ndc" => 3
"jumper" => 1
]
}
1 => Illuminate\Support\Collection {#4570
#items: array:4 [
"id" => 57
"first_name" => "qwer"
"last_name" => "Martian"
"ndc" => 3
]
}
2 => Illuminate\Support\Collection {#4588
#items: array:4 [
"id" => 58
"first_name" => "asdf"
"last_name" => "Martian"
"jumper" => 1
]
}
]
toJson(): Transforms collection into json string: (I've added line breaks for better readability)
[
{
"id":59,
"first_name":"Steve",
"last_name":"Martian",
"ndc":3,
"jumper":1
},
{
"id":57,
"first_name":"qwer",
"last_name":"Martian",
"ndc":3
},
{
"id":58,
"first_name":"asdf",
"last_name":"Martian",
"jumper":1
}
]
I need help for my timesheets app model …
What I want to do is an app that allows you to save your times for every day.
I built this model :
Note : I added the hasMany relation from timesheets to timesheets_tasks model because I didn’t manage to access days table by another way
At the end I am not sure the belongsToMany from timesheets to tasks is really appropriate in this situation …
I have 2 questions :
Does this model sound good to you ?
How to save days ?
Because when I save this :
object(App\Model\Entity\Timesheet) {
'id' => (int) 2,
'user_id' => (int) 1,
'week_nb' => (int) 16,
'year' => (int) 2018,
'validated' => false,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-02T09:25:29+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-28T22:17:29+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'tasks' => [
(int) 0 => object(App\Model\Entity\Task) {
'id' => (int) 1,
'name' => 'Atelier 1 - Compta',
'scope' => 'global',
'user_id' => null,
'project_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:19:44+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:19:44+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tasks'
},
(int) 1 => object(App\Model\Entity\Task) {
'id' => (int) 2,
'name' => 'Atelier 2 - Achats',
'scope' => 'global',
'user_id' => null,
'project_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:20:13+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:20:13+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tasks'
}
],
'timesheets_tasks' => [
(int) 0 => object(App\Model\Entity\TimesheetsTask) {
'id' => (int) 13,
'timesheet_id' => (int) 2,
'task_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-28T22:04:45+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-28T22:04:45+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'days' => [
(int) 0 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-16 22:17:40.643236'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 1 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-17 22:17:40.647586'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 2 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-18 22:17:40.647655'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 3 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-19 22:17:40.647705'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 4 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-20 22:17:40.647754'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 5 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-21 22:17:40.647796'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
},
(int) 6 => object(App\Model\Entity\Day) {
'date' => object(DateTime) {
date => '2018-04-22 22:17:40.647838'
timezone_type => (int) 3
timezone => 'UTC'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
}
],
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'days' => true
],
'[original]' => [
'days' => []
],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'TimesheetsTasks'
}
],
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'tasks' => true
],
'[original]' => [
'tasks' => [
(int) 0 => object(App\Model\Entity\Task) {
'id' => (int) 1,
'name' => 'Atelier 1 - Compta',
'scope' => 'global',
'user_id' => null,
'project_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:19:44+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-01T15:19:44+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'_joinData' => object(App\Model\Entity\TimesheetsTask) {
'id' => (int) 13,
'timesheet_id' => (int) 2,
'task_id' => (int) 1,
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-28T22:04:45+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-04-28T22:04:45+00:00',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'TimesheetsTasks'
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tasks'
}
]
],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Timesheets'
}
I doesn’t save days … The controller :
public function add()
{
$timesheet = $this->Timesheets->newEntity();
if ($this->request->is(['patch', 'post', 'put'])) {
if(!is_null($this->request->getData('id'))) {
$timesheet = $this->Timesheets->get($this->request->getData('id'), ['contain' => ['TimesheetsTasks' => ['Days'], 'Tasks']]);
}
// Here I "create" 7 days entities if a new timesheet_task has been added
$this->initEmptyTasks($timesheet);
$timesheet = $this->Timesheets->patchEntity($timesheet, $this->request->getData(), ['associated' => ['TimesheetsTasks' => ['associated' => ['Days']], 'Tasks']]);
if ($result = $this->Timesheets->save($timesheet)) {
$this->Flash->success(__('The timesheet has been saved.'));
return $this->redirect($this->referer());
}
$this->Flash->error(__('The timesheet could not be saved. Please, try again.'));
return $this->redirect($this->referer());
}
$users = $this->Timesheets->Users->find('list', ['limit' => 200]);
$this->set(compact('timesheet', 'users'));
}
I don’t have any error, but days are not saved …
Thanks in advance for your help.
Oh my god I found the root cause …
I was modifying the entity instead of the data I was patching the entity with …
But now I have one question :
Can you have a BTM pivot table to have hasMany relation with another Table ?
Because I can save _joinData for additionnal field (comment field here), but not for hasMany relation (days table)
Here’s my $data before I call the patchEntity method :
[
'id' => '2',
'week_nb' => '16',
'year' => '2018',
'tasks' => [
(int) 0 => [
'id' => (int) 1,
'_joinData' => [
'comment' => 'Hihi tache 1 new',
'days' => [
(int) 0 => [
'timesheet_task_id' => (int) 35,
'ts_date' => object(DateTime) {
date => '2018-04-16 19:04:45.150726'
timezone_type => (int) 3
timezone => 'Europe/Paris'
}
]
]
]
],
(int) 1 => [
'id' => (int) 2,
'_joinData' => [
'comment' => 'Hihi tache 2'
]
]
]
]
Here’s my data after I call the patchEntity method :
$timesheet = $this->Timesheets->patchEntity($timesheet, $data, ['associated' => ['Tasks._joinData.Days']]);
The _joinData part :
'_joinData' => object(App\Model\Entity\TimesheetsTask) {
'id' => (int) 35,
'timesheet_id' => (int) 2,
'task_id' => (int) 1,
'comment' => 'Hihi tache 1 new',
'created' => object(Cake\I18n\FrozenTime) {
'time' => '2018-05-01T18:39:17+02:00',
'timezone' => 'Europe/Paris',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\FrozenTime) {
'time' => '2018-05-01T18:58:55+02:00',
'timezone' => 'Europe/Paris',
'fixedNowTime' => false
},
'days' => [
(int) 0 => object(App\Model\Entity\Day) {
'timesheet_task_id' => (int) 35,
'ts_date' => object(DateTime) {
date => '2018-04-16 19:02:01.023189'
timezone_type => (int) 3
timezone => 'Europe/Paris'
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'timesheet_task_id' => true,
'ts_date' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Days'
}
],
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'days' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'TimesheetsTasks'
},
It seems to be working as cakePHPis converting my day record to a day entity, but same result now, it’s not saving.
I would like to save data this way: 'Orders'->'OrdersFoods'->'OrdersFoodsChanges'. Where OrdersFoodsTable is a join table (Orders - Foods).
Why do I want to do so? Table OrdersFoods contains information about ordered foods (ids), but I would like to save additional data about every ingredient used for that food in particular order. That's why I've created additional table (OrdersFoodsChanges) with id from table OrdersFoods.
I can save data to a joined table but not to another table (OrdersFoodsChangesTable).
I've tried to do it the simplest way:
$order = $this->Orders->patchEntity($order, $this->request->data, [
'associated' => [
'Foods._joinData.OrdersFoodsChanges'
//'Foods.OrdersFoods.OrdersFoodsChanges'
]
but with no luck.
patchEntity object
object(Admin\Model\Entity\Order) {
'user_id' => (int) 1,
'city' => '',
'postal_code' => '',
'street' => '',
'house' => '',
'phone' => '',
'foods' => [
(int) 0 => object(Admin\Model\Entity\Food) {
'id' => (int) 15,
'name' => 'Royal',
'price' => (float) 15,
'category_id' => (int) 2,
'photo_id' => (int) 69,
'shop_id' => (int) 1,
'favorite' => false,
'vat' => (int) 23,
'visible' => true,
'position' => (int) 0,
'_joinData' => object(Admin\Model\Entity\OrdersFood) {
'quantity' => (int) 1,
'orders_foods_changes' => [
(int) 0 => object(Admin\Model\Entity\OrdersFoodsChange) {
'component_quantity' => (int) 25,
'component_id' => (int) 1,
'order_food_id' => (int) 1,
'type' => 'ADD',
'element_num' => (int) 1,
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'component_quantity' => true,
'component_id' => true,
'order_food_id' => true,
'type' => true,
'element_num' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Admin.OrdersFoodsChanges'
}
],
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'quantity' => true,
'orders_foods_changes' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Admin.OrdersFoods'
},
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'_joinData' => true
],
'[original]' => [
'_joinData' => [
'quantity' => '1',
'orders_foods_changes' => [
(int) 0 => [
'component_quantity' => (int) 25,
'component_id' => (int) 1,
'order_food_id' => (int) 1,
'type' => 'ADD',
'element_num' => (int) 1,
'id' => (int) 2
]
]
]
],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Admin.Foods'
}
],
'price' => (float) 15,
'shop_id' => (int) 1,
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'user_id' => true,
'type' => true,
'city' => true,
'postal_code' => true,
'street' => true,
'house' => true,
'phone' => true,
'foods' => true,
'price' => true,
'shop_id' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Admin.Orders'
}
As I can see from sql log Cake does not even tries to save anything to a last table.
I would be grateful for any help.
So the main problem was that by default cake save method makes only one level association.
By default the save() method will also save one level of associations
When I've added associated it all went well.
if ($this->Orders->save($order, [
'associated' => ['Foods._joinData.OrdersFoodsChanges']
]))
I'm currently trying to join 5 tables to retrieve a list of ordered items. Is it possible to have all the items together rather than having multiple collections of 'ordered_packs' with a different ItemID?
My code
if(!\App\OrderedPack::where('orderID', '=', $orderID)->get()->isEmpty()) {
$packs = \App\OrderedPack::where('ordered_packs.orderID', '=', $orderID)
->join('packs', 'ordered_packs.packID', '=', 'packs.packID')
->join('pack_items', 'packs.packID', '=', 'pack_items.packID')
->join('items', 'pack_items.itemID', '=', 'items.itemID')->get();
my returned result
Collection {#288 ▼
#items: array:2 [▼
0 => OrderedPack {#289 ▼
#primaryKey: "ordered_packID"
#table: "ordered_packs"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:30 [▼
"ordered_packID" => 1
"orderID" => 80
"packID" => 7
"pack_quantity" => 12
"created_at" => "2016-02-11 14:20:29"
"updated_at" => "2016-03-08 16:21:02"
"pack_name" => "niceee"
"pack_description" => ""
"pack_itemID" => 5
"itemID" => 9
"item_code" => "INT T-C 12/15"
"companyID" => 1
"item_name" => "International Terms and Conditions"
"initial_level" => 5000
"current_level" => 4000
"item_typeID" => 6
"productID" => 1
"image_location" => "9.pdf"
"box_total" => 100
"offsite_level" => 1000
"locationID" => 1
"language" => "English"
"download_url" => ""
"archived" => 0
"low_stock_level" => 1000
"change_description" => ""
"old_itemID" => 0
"pdf_only" => 0
"total_stock" => 5000
"groupID" => 2
]
#original: array:30 [▶]
#relations: []
#hidden: []
#visible: []
#appends: []
#fillable: []
#guarded: array:1 [▶]
#dates: []
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
}
1 => OrderedPack {#290 ▼
#primaryKey: "ordered_packID"
#table: "ordered_packs"
#connection: null
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:30 [▼
"ordered_packID" => 1
"orderID" => 80
"packID" => 7
"pack_quantity" => 12
"created_at" => "2016-02-11 14:18:35"
"updated_at" => "2016-03-08 16:21:02"
"pack_name" => "niceee"
"pack_description" => ""
"pack_itemID" => 6
"itemID" => 7
"item_code" => "GW CHO INT BRO 12/15"
"companyID" => 1
"item_name" => "GW Choice International Brochure (Including Details Guide) 12/15"
"initial_level" => 5000
"current_level" => 234
"item_typeID" => 1
"productID" => 2
"image_location" => "7.pdf"
"box_total" => 100
"offsite_level" => 2304
"locationID" => 1
"language" => "English"
"download_url" => ""
"archived" => 0
"low_stock_level" => 1000
"change_description" => ""
"old_itemID" => 0
"pdf_only" => 0
"total_stock" => 2538
"groupID" => 2
]
I have an orderedpack instance for every item in a pack. I would like one ordered pack instance that contains multiple items.
The tables
order->ordered_packs->packs->pack_items->items
In one query is not possible to take advantage of Laravel, but you could take advantage of eager loading:
$packs = \App\OrderedPack::where('ordered_packs.orderID', '=', $orderID)
->with('packs.pack_items.items')
->get();
Assuming your relationships are setup correctly
I'm playing with RoseDB::Object on the employees test dataset, and for some reason, I can't get my foreign key relationships ('department' and 'employee') to work on the DeptEmp object. (Class structure below).
When I try $e->dept_emp->[0]->department, I get:
Can't locate object method "department" via package "My::FakeEmployees::DeptEmp"
Methods for the following relationships and foreign keys were deferred and
then never actually created in the class My::FakeEmployees::DeptEmp.
TYPE NAME
---- ----
Foreign Key department
Foreign Key employee
I'm sure I have something set up wrong in my class structure, but what?
CLASS STRUCTURE (some classes omitted for clarity):
I created the various objects using the instructions in the RDBO tutorial:
package My::FakeEmployees::Employee;
use strict;
use base qw(My::FakeEmployees::DB::Object);
__PACKAGE__->meta->setup(
table => 'employees',
columns => [
emp_no => { type => 'serial', not_null => 1 },
birth_date => { type => 'date', not_null => 1 },
first_name => { type => 'varchar', length => 14, not_null => 1 },
last_name => { type => 'varchar', length => 16, not_null => 1 },
gender => { type => 'enum', check_in => [ 'M', 'F' ], not_null => 1 },
hire_date => { type => 'date', not_null => 1 },
],
primary_key_columns => ['emp_no'],
'relationships' => [
'departments' => {
'type' => 'many to many',
'map_class' => 'My::FakeEmployees::DeptEmp',
},
'dept_emp' => {
'type' => 'one to many',
'class' => 'My::FakeEmployees::DeptEmp',
'column_map' => { 'emp_no' => 'emp_no' },
},
'dept_manager' => {
'type' => 'one to many',
'class' => 'My::FakeEmployees::DeptManager',
'column_map' => { 'emp_no' => 'emp_no' },
},
'salaries' => {
'type' => 'one to many',
'class' => 'My::FakeEmployees::Salary',
'column_map' => { 'emp_no' => 'emp_no' },
},
'titles' => {
'type' => 'one to many',
'class' => 'My::FakeEmployees::Title',
'column_map' => { 'emp_no' => 'emp_no' },
},
],
);
__PACKAGE__->meta->make_manager_class('employees');
1;
package My::FakeEmployees::DeptEmp;
use strict;
use base qw(My::FakeEmployees::DB::Object);
__PACKAGE__->meta->setup(
table => 'dept_emp',
columns => [
dept_no => { type => 'character', not_null => 1 },
emp_no => { type => 'integer', not_null => 1 },
from_date => { type => 'date' },
to_date => { type => 'date' },
],
primary_key_columns => [ 'emp_no', 'dept_no' ],
foreign_keys => [
department => {
class => 'My::FakeEmployees::Departments',
key_columns => { dept_no => 'dept_no' },
},
employee => {
class => 'My::FakeEmployees::Employees',
key_columns => { emp_no => 'emp_no' },
},
],
);
__PACKAGE__->meta->make_manager_class('dept_emp');
1;
package My::FakeEmployees::Department;
use strict;
use base qw(My::FakeEmployees::DB::Object);
__PACKAGE__->meta->setup(
table => 'departments',
columns => [
dept_no => { type => 'character', length => 4, not_null => 1 },
dept_name => { type => 'varchar', length => 40, not_null => 1 },
],
primary_key_columns => ['dept_no'],
unique_key => ['dept_name'],
'relationships' => [
'employees' => {
'type' => 'many to many',
'map_class' => 'My::FakeEmployees::DeptEmp',
},
],
);
__PACKAGE__->meta->make_manager_class('departments');
1;
Your foreign key has a typo:
foreign_keys => [
department => {
class => 'My::FakeEmployees::Departments',
That should be 'Department', not 'Departments'
It turned out to be a mistake in my code. These lines in DeptEmp.pm:
foreign_keys => [
department => {
class => 'My::FakeEmployees::Departments',
key_columns => { dept_no => 'dept_no' },
},
employee => {
class => 'My::FakeEmployees::Employees',
key_columns => { emp_no => 'emp_no' },
},
],
Have incorrect class names. It should be My::FakeEmployees::Employee and My::FakeEmployees::Department. Singular, not plural.