CakePHP 3 Retrieve associated data - cakephp

I want to get Shop data and also associated Comments with this Shop, but Comments contains also author_id which connected to Authors table.
How to get also not only Comments, but also and Authors data by author_id?
Its my Shops controller:
public function viewShop($slug = null)
{
$shop = $this->Shops->find('all')
->where(['slug'=>$slug])
->contain(['Comments']);
$this->set('shop', $shop);
$this->render('index');
}

First bind your Comment model with Author model as BelongsTo and then replace your code with this.
public function viewShop($slug = null)
{
$shop = $this->Shops->find('all')
->where(['slug'=>$slug])
->contain(array('Comment'=>array("Author")));
$this->set('shop', $shop);
$this->render('index');
}

Related

Dapper One to Many Mapping Logic

The dapper tutorial gives this example to help a user with Multi Mapping (One to Many)
While this works I am curious why they have you store the orders in the dictionary but then in the end they use a linq.Distinct() and return from the list. It seems like it would be cleaner to just return the ordersDictionary.Values as the dictionary logic ensures no duplicates.
//Tutorial
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
List<Order> list = connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID")
.Distinct()
.ToList();
return list;
}
//my suggestion
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
//change 1 no need to store into list here
connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID"); //change 2 remove .Distinct().ToList()
return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
}
I'm the author of this tutorial: https://dapper-tutorial.net/query#example-query-multi-mapping-one-to-many
why they have you store the orders in the dictionary
A row is returned for every OrderDetail. So you want to make sure to add the OrderDetail to the existing Order and not create a new one for every OrderDetail. The dictionary is used for performance to check if the Order has been already created or not.
it would be cleaner to just return the ordersDictionary.Values
How will your query return dictionary values?
Of course, if you are in a method such as yours, you can do
var list = orderDictionary.Values;
return list;
But how to make this Connection.Query return dictionary values? An order is returned for every row/OrderDetail, so the order will be returned multiple times.
Outside the Query, your dictionary solution works great and is even a better solution for performance, but if you want to make your Query return the distinct list of orders without using Distinct or some similar method, it's impossible.
EDIT: Answer comment
my suggestion return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
Thank you for your great feedback, it's always appreciated ;)
It would be weird in a tutorial to use what the query returns when there is no relationship but use the dictionary for one to many relationships
// no relationship
var orders = conn.Query<Order>("", ...).Distinct();
// one to many relationship
conn.Query<Order, OrderDetail>("", ...);
var orders = orderDictionary.Values.ToList();
Your solution is better for performance the way you use it, there is no doubt about this. But this is how people usually use the Query method:
var orders = conn.Query("", ...).Distinct();
var activeOrders = orders.Where(x => x.IsActive).ToList();
var inactiveOrders = orders.Where(x => !x.IsActive).ToList();
They use what the Query method returns.
But again, there is nothing wrong with the way you do it, this is even better if you can do it.

Grails GORM - return count of objects who's array property > 0

Consider the following;
class Person {
int id
String name
static hasMany = [cars : Car]
}
class Car {
int id
String brand
static belongsTo = Person
static hasMany = [owners: Person]
}
The above will result in a person_cars join table. All I'm trying to find out is if there are any entries in that table, in words;
Do any Persons currently exist who own a car.
Happy to use any mechanism available (finders/criteria/HQL etc)
My opinion is better to add PersonCar entity.
For your question:
Car.count() > 0
As car belongTo Person, so that can't be any copy of car without been added to the person.
If person is nullable you can use:
Car.countByPersonIsNotNull()
Think that if car has person, so that there will be a value in that table.
Got it! A lot simpler than I thought.
Person.createCriteria().count {
owners {
count
}
}
This effectively gives me the number of person_cars records.

How can I fetch this info in laravel Eloquent

Sorry for the question title but I can't really find an suitible title.
I use Laravel 3 with Eloquent models (first project in Laravel).
I have an user, list and item model. The user can't fetch any lists, the list can fetch items. But the user can order the items, so the order is saved per user/item.
Will be more clear with following data (simplified for clarity):
Database table:
USER
--------
id
name
LIST
-------
id
name
ITEM
-------
id
list_id
name
USER_ITEM_ORDER
---------------
user_id
item_id
order
List model:
class List extends Eloquent {
public function items() {
return $this->has_many('Item');
}
}
Now I want to use the list model to get all the items based on an user, $list->items($user_id) --> array with items with the user's order.
Can someone show me the way to achieve this?
Please try using this code :
public static function NAMEUserId() {
$query = DB::table('username')
->select(array('users.username', DB::raw('NAME(user's.id) as UserID')))
->join('users', 'items.user_id', '=', 'users.id')
->group_by('users.id')
->order_by('usersid', 'desc')
->get();
return $query;
}
Yeah! I fixed it. :D I'll answer my own question because there is an up vote..
It was pretty easy, just use Fluent on the returned object like this:
public function items($user_id = 0) {
$items = $this->has_many('Item');
// do we need to get the order?
if($user_id != 0) {
$items = $items->left_join('user_item_order', 'items.id', '=', 'user_item_order.item_id')
->where('user_item_order.user_id', '=', $user_id)
->order_by('user_item_order.order', 'asc');
}
return $items->get();
}

CakePHP: Is it possible to insert record with pre-defined primary key value?

I have a CakePHP model - User - that has ties to an external corporate system. I store some data on those systems and other data locally. In my User::beforeSave() method, I'm trying to set an ID, send the data (with that custom ID) to my corporate systems and then, if it inserts successfully there, return true so that Cake will insert the new user record with that same ID so that I can link them later.
I can't find a way to make this happen. Is there a way to insert a CakePHP record with a user-specified primary key value? I'm using UUIDs so there's (effectively) no opportunity for overlap.
$this->data['User']['id'] = String::uuid()
try {
$user_proxy = new CoreServicesUserProxy();
$corp_user = $user_proxy->CreateUser (
array (
'user' => array (
'UserName' => 'myusername',
'EmailAddress' => $this->data['User']['email'],
'SecurityId' => $this->data['User']['id']
)
)
);
}
catch ( Exception $e ) {
// error handling stuff
return false;
}
I realise you have already been given some hints, but here is some code which might help.
Why not add an external_user_id field to your users table?
<?php
class User extends AppModel {
function beforeSave() {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->createUser($this->data);
if (!$externalUser) {
return false;
}
$this->data['User']['external_id'] = $externalUser['id'];
return true;
}
function afterFind($results, $primary) {
// handle different types of find here ('all' vs 'first' vs through relation)
foreach ($results as &$result) {
$result = $this->_mergeExternalUser($result);
}
}
function _mergeExternalUser($user) {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->retrieveUser($result['external_id']);
return am($externalUser, $user);
}
}
?>
There is a way - but typically you would add another column to the Users table instead and let CakePHP do it's thing with the primary key. See this Bakery article to know how it's done. Since it is more than a year later, this is for reference mostly. As far as I understand it, this should function well with CakePHP 1.2 as well.

Model won't update

Using cakephp:
I am trying to update customer information and the address the customer is linked to.
such that Customer.address_id = Address.id, and
Customer Model
$belongsTo = 'Address';
From the customers_controller
function profile($id = null)
{
if (empty($this->data['Customer']))
{
$this->Customer->id = $id;
$this->data = $this->Customer->read();
}
else
{
$this->Customer->id = $this->data['Customer']['id'];
$this->Customer->read();
$this->Customer->save($this->data['Customer']);
$this->Customer->Address->save($this->data['Address']);
}
}
Customer correctly updates, but Address always inserts a new row. How do I get this address to update?
first of all, take away lines 11 and 12. those serve no purpose. make sure your view contains form elements for Customer.id and Address.id. If you are just updating the Address you dont need line 13 either. The short answer is that Cakephp will insert row instead of update if the primary key is missing. In your case this means [Address][id].

Resources