I've been having a look at phalcon as an alternative to a laravel project that I'm running. It's mainly a REST api. I'm trying to figure out how to include relationships in a json response for models.
For example, if I had 2 models, set up like below:
class Clients extends \Phalcon\Mvc\Model
{
public function initialize() {
$this->hasMany('clientid','Contacts','clientid')
}
}
class Contacts extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('clientid','Clients','clientid')
}
}
And I did a:
$clients = Client::find();
return json_encode($clients->toArray());
How would I get it to automatically include the contacts?
I need to output to be something like this:
[{
clientid:'1111',
contacts:[
{
contactid:111
}
]
}];
Many thanks!
From the docs:
By accessing an attribute with the same name as the relationship will retrieve all its related record(s).
Based on the above, you should be able to access the contacts via the relationship defined like so:
$clients = Client::find();
$clientContacts = $clients->contacts;
// do something with the contacts
Edit:
Try to loop through your $contacts and then get the related record(s):
foreach ($clients as $client) {
// Do something with the contacts...
$contacts[$client->id] = $client->contacts;
}
Related
I have a very simple scenario where I'm receiving a list of Variance Positions from the end user. To be able to validate the structure of the input, I created the following model for the single item that I should receive:
class VariancePositionsForm extends Model{
public $id;
public $position;
public function rules()
{
return [
[['id','position'], 'required'],
[['id', 'position'], 'integer'],
];
}
}
And in the controller, I have the following:
$variancePositions = [];
for($i=0;$i<sizeof(Yii::$app->request->post());$i++)
{
$variancePositions[] = new VariancePositionsForm();
}
VariancePositionsForm::loadMultiple($variancePositions, Yii::$app->request->post());
When I try to var_dump($variancePositions) however, I'm finding that its empty. In other words, loadMultiple() is not loading the models. What am I doing wrong?
Because you don't load the model from the form, only from json you have to add an empty string into the last parameter in this function:
VariancePositionsForm::loadMultiple($variancePositions, Yii::$app->request->post(), '');
look here:
https://github.com/yiisoft/yii2/blob/master/framework/base/Model.php#L884
I have this ERD below:
Relations are as follow,
Entity to Host is One to One
Host to Portal is One to Many ( One Host to Many Portal )
class Entity extends Model
{
public function initialize()
{
$this->hasOne(
"entity_id",
"Host",
"entity_id"
);
}
}
class Host extends Model
{
public function initialize()
{
$this->belongsTo(
"entity_id",
"Host",
"entity_id"
);
$this->hasMany(
"host_id"
"Portal",
"host_id"
);
}
}
class Portal extends Model
{
public function initialize()
{
$this->belongsTo(
"portal_id",
"Host,
"portal_id"
);
}
}
When i try to retrieve the list of Portals base on the array of entity id provided, i got a fatal error.
Fatal error: Call to undefined method
Phalcon\Mvc\Model\Resultset\Simple::getHost()
This is my controller code to retrieve the portals
$hostObj = Host::find(['entity_id IN ({ids:array})',
'bind' => array('ids' => $entity_id)]);
if($hostObj)
{
$portals = $hostObj->Portal;
}
Basically the goal is to retrieve the list of Portals base on the array of entity_id provided. But i really wonder, whats wrong with my model relations that lead into the fatal error.
Find method returns Resultset, you need either to use findFirst or select row from Resultset(like array-like style $portals[0], or $portals->getFirst() or $portals->offsetGet('0')
Reading the documentation in Breeze website, to retrieve a single entity have to use the fetchEntityByKey.
manager.fetchEntityByKey(typeName, id, true)
.then(successFn)
.fail(failFn)
Problem 1: Metadata
When trying to use this method, an error is displayed because the metadata has not yet been loaded. More details about the error here.
The result is that whenever I need to retrieve a single entity, have to check if the metadata is loaded.
manager = new breeze.EntityManager(serviceName);
successFn = function(xhr) {}
failFn = function(xhr) {};
executeQueryFn = function() {
return manager.fetchEntityByKey(typeName, id, true).then(successFn).fail(failFn);
};
if (manager.metadataStore.isEmpty()) {
return manager.fetchMetadata().then(executeQueryFn).fail(failFn);
} else {
return executeQueryFn();
}
Question
How can I extend the breeze, creating a Get method to check if metadata is loaded, and if not, load it?
Problem 2: OData and EntitySetController
I would use the OData standard (with EntitySetController) in my APIs.
This page in Breeze documentation shows how, then follow this tutorial to deploy my app with OData.
The problem as you can see here and here, is that the EntitySetController follows the odata pattern, to retrieve an entity must use odata/entity(id), or to retrieve all entities you can use `odata/entity'.
Example
In controller:
[BreezeController]
public class passosController : EntitySetController<Passo>
{
[HttpGet]
public string Metadata()
{
return ContextProvider.Metadata();
}
[HttpGet, Queryable(AllowedQueryOptions = AllowedQueryOptions.All, PageSize = 20)]
public override IQueryable<T> Get()
{
return Repositorio.All();
}
[HttpGet]
protected override T GetEntityByKey(int key)
{
return Repositorio.Get(key);
}
}
When I use:
manager = new breeze.EntityManager("/odata/passos");
manager.fetchEntityByKey("Passo", 1, true)
.then(successFn)
.fail(failFn)
The url generated is: /odata/passos/Passos?$filter=Id eq 1
The correct should be: /odata/passos(2)
Question
How can I modify Breeze for when use fetchEntityByKey to retrieve entity use odata/entity(id)?
I am trying to get eager loading working when fetching related models.
public function allCompanies() {
$companies = $this->companies()->active()->get();
$companies->load('Industry');
return $companies;
}
I have this function on the model Industry and I believe this should fetch the companies which are within the current industry, it should also fetch the related industry for the companies (this will be the current record)
This doesn't seem to work as when I iterate over the companies it re-fetches the Industry for each one.
Am I doing something wrong with the $companies->load('Industry'); line?
Thanks
Try:
public function allCompanies() {
$companies = $this->companies()->active()->with('industry')->get();
return $companies;
}
The with() and load() functions are referencing functions within the model not the model itself ie:
class Company extends Eloquent {
public function industry()
{
return $this->belongsTo('Industry');
}
}
class Industry extends Eloquent {
public function companies()
{
return $this->hasMany('Company');
}
}
Please reference http://laravel.com/docs/eloquent#eager-loading
I am a bit lot about what to do in an OO/DB relation...
Here is the DB model :
CREATE TABLE User
Id
CREATE TABLE Location
userId
// EDIT oups, wrong !
// placeId
// Should be :
seatId
CREATE TABLE Game
locationId
Now some code :
class User
{
private Location locations[]; // need this for several reasons...
public function loadFromDatabase()
{
// Load data from DB
// ...
result = DB::query("SELECT Id FROM Locations WHERE userId="+this->Id);
foreach(result)
{
l = new Location();
l->loadFromDatabase(result);
locations[] = l;
}
}
}
class Location
{
private User user;
public function loadFromDatabase()
{
...
}
}
class Game
{
private Location location;
public loadFromDatabase()
{
/*
Here comes the problem :
how to have a reference to a location
created by the User class ?
*/
}
}
A User play Games in several Locations.
EDIT : And for each location the user plays on seat. Or on another seat...
When I want to know where a game has been played I access Game.location. And when I want to know who played it, I access Game.location.user
Here is my problem : I want the Game.location to be the same reference to one of the User.locations and I do not know how to do this...
And, globally, I feel something wrong about my code...
Any help ?
Thanks
Since you have a placeId in your Location table, I assume there is a Place table which describes what the places actually are, while the Location table simply represents the many-to-many mapping between users and places.
In that case, Location doesn't need to have an Id of its own and doesn't need to be a class, but Place does.
To load just one instance of each object from the database, cache the instances in a static map inside each class.
class Place
{
// Static
private static Place loadedPlaces[];
public static function get(id)
{
if (!loadedPlaces[id])
{
loadedPlaces[id] = new Place(id);
loadedPlaces[id]->loadFromDatabase();
}
return loadedPlaces[id];
}
// Non-static
private id;
public function loadFromDatabase()
{
// ...
}
}
Then to get references to places for the properties of a user or a game, you just access them via the static method.
class User
{
public function loadFromDatabase()
{
result = DB::query("SELECT placeId FROM Locations WHERE userId="+this->Id);
foreach(result)
{
places[] = Place::get(result);
}
}
}
class Game
{
public function loadFromDatabase()
{
place = Place::get(place);
}
}
This uses:
Lazy initialization, because places are only loaded when they are needed.
Multiton pattern, because there is only one instance of each place by id.
Not quite a factory method, because there's no object hierarchy involved.