php Yii Nested json array - php

First code:
$result=ResPlaces::model()->findall($criteria);
/*foreach($result as $value)
{
$model_name=ResRegistration::model()->findByAttributes(array('id'=>$value->user_id));
$model_image=ResImages::model()->findByAttributes(array('place_id'=>$value->id),array('limit'=>'1'));
}*/
}
echo CJSON::encode($result);
?>
i need to add model_name->company_name & $model_image->image
to my echo json array

Try to load the relations within the findAll so you don't need the foreach.
ResPlaces::model()->with('ResRegistration', 'ResImages')->together()->findAll($criteria);
For ResRegistration and ResImages use the relation names as defined in your model.
If you don't need all fields of these relations, you can specify a select in your $criteria.
See the guide for more info.
edit: I am not quite sure, why you cannot use relation. However you will need a loop, like you already have. Here is how you do it without relations:
First add the fields you want to use to your model. In this case
class ResPlaces extends CActiveRecord
{
public $name; // check that these do not collide with your models db fields
public $image;
[...]
}
In your controller do the loop like you did before:
foreach($result as $key => $value)
{
$result[$key]->name = ResRegistration::model()->findByAttributes(array('id' => $value->user_id));
// findByAttributes returns only one record, so you don't need the limit here
// if you want multiple records, you have to use findAllByAttributes
$result[$key]->image = ResImages::model()->findByAttributes(array('place_id' => $value->id), array('limit' => '1'));
}
That should do it. However I wouldn't recommend this way, because you have lots of additional database requests. If your $result is populated with say 100 records, you have in sum 200 additional queries which are not nessassary with a relation.
Note also that if you need these two other fields more often, it may be better to put these two queries which are now in the controller in your model. The afterFind() would be the right place.

Related

Fetch data from many to many relationship with eloquent

I have 2 models, store and dvd with many to many relationship
dvd model:
public function stores() {
return $this->belongsToMany('App\store');
}
store model:
public function dvds ( ) {
return $this->belongsToMany('App\dvd');
}
Then, in controller, when I need fetch data from both models, I use code like this:
$res_store = store::orderBy("id","desc")->get();
foreach( $res_store as $row ) {
$dvds = $row->dvds()->get();
foreach ($dvds as $dvd) {
// echo columns here
}
}
This works, but my question is, I'm doing this in correct way? I'm asking because it seems 2 loops for this simple relation is somehow inefficient.
No, this is not the right way.
When looping over a $store, you can access the associated $dvd records via $store->dvds; there is no need to call $row->dvds()->get(), as that is executing a new query with the same result of $store->dvds. Full code should simply be:
$stores = Store::with("dvds")->orderBy("id", "DESC")->get();
foreach($stores AS $store){
foreach($store->dvds AS $dvd){
... // Do something with `$dvd`
}
}
The ::with("dvds") clause is known as "Eager loading", and prevents $store->dvds from needing execute another query behind the scenes.
Also, please name your models correctly. Classes in PHP are StudlyCase, so Store and DVD, not store and dvd.

laravel orm: different result from almost same query

I've a very big doubt about how works laravel for a very simple thing:
If I call:
$companies=User::All();
Then I can use statement like this in a forach:
foreach($companies as $company)
$company['new_field']= 'something';
If i'm limiting the output of the query like:
$companies = DB::table('companies')
->select('id','name','email','business_name',...)->get();
The things doesnt work as before,
I try with or without the ->get()
I try to convert with ->toArray() (errors rised)
I try with put() and push() for collections method and agains errors...
How can I add a field in every item of the collection just to pass it to a view?
Try like this, hope it works for you:
$users=User::select('id','name','email','business_name',...)->get()->toArray();
and then use foreach loop like this:
foreach($users as $key => $value ){
$users[$key]['newField'] = "Demo";
}
If you are using Laravel and model in it so there is a better way to add custom attribute or field here is what i do for custom field
For Example :
There is a Model Name User
so in User Model
add a property name appends like :
class User extends Model
{
protected $appends = ['new_field'];
public function getNewFieldAttribute() // defining field logic here
{
return // your code
}
So you no need to use foreach and looping and adding new field
for more have a look on laravel doc : https://laravel.com/docs/5.5/eloquent-mutators#accessors-and-mutators
Suggestion
you can limit your output with Model too.
User::select('id','name','email','business_name',...)->get();
if you are making an array like
User::select('id','name','email','business_name',...)->get()->toArray();
so this will also give you your custom field

Can I have a field that combines multiple MySQL columns into one array in a Laravel Model

I am currently trying to construct a Laravel model based on a list of courses and course sections. For each record in the Course Sections database table, there are 7 columns for each day of the week that it might be on.
CourseID|SectionNumber|...|Mon|Tue|Wed|Thu|Fri|Sat|Sun|
-------------------------------------------------------
1001 | 01 |...|Mon| 0 |Wed| 0 | 0 | 0 | 0 |
Admittedly, it's not the best designed database (it should probably be booleans, and would also probably make sense as a relations table rather than in the same large table) but I don't have direct control over that part.
But what I would like to do is have all of those days stored in an array under one value $days in the Course model, however I'm not quite sure where to go about it. My end goal is to be able to Serialize a CourseSection Model into a JSON feed.
$course->days;
//should return [Mon, Wed]
Right now, in my CourseController, I have the following code that constructs that array as it feeds it to the View based off of a raw query
$days = array_where([$result->isMon, $result->isTue, $result->isWed, $result->isThu, $result->isFri, $result->isSat, $result->isSun],
function ($key, $value)
{
return $value != "0";
});
$courses[$course_key]['sections'][$result->section_number]->days = $days;
I suppose my question is can I do this all in one model? Is it possible to have variables equal an array? Or should this stay in the Controller and have the Model as a direct representation of the structure in the database?
You can use Accessors and Mutators to append extra data to your Eloquent models.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model as Eloquent;
class Course extends Eloquent
{
protected $appends = ['days'];
public function getDaysAttribute()
{
$values = [];
// Build your array of data from your existing database structure here...
return $values;
}
}
You can then refer to this new item without additional work in your controllers:
$course = Course::find(1);
$array = $course->days; // This value will come from the getDaysAttribute() value in the Eloquent model
Note: I haven't tested this code, but I've taken it from one of my projects built in Laravel 5.1 which utilises these methods. I haven't written the code for generating the $values array, but you should be able to put that together based on your existing code.
Alternatively, if you wanted to modify your database structure, you could look at using Array Casting which would automatically serialise/deserialise an array for storing as text in the database.
class Course extends Model {
public $appends = ['days'];
public function getDaysAttribute(){
$days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
$course = this;
return array_where($days,function($key, $val) use ($course)){
return $course[$value] != 0;
}
}
}
Then you can use $course->days, and when you serialize to JSON the days attribute will be included courtesy of the appends attribute.

CakePHP add model name to information returned in queries

I want to include the model name in the returned results of a query using CakePHP's find() methods.
For instance, if I do a
$person = $this->Person->find("first", array(
"conditions" => array (
"Person.id" => $id
)
));
I get back
Person{id:1, name:Abraham Lincoln}
I want to get back
Person{id:1, name:Abraham Lincoln, model: Person}
I'm fairly front-end oriented. I know I could loop through results and add these at the controller level, but that seems tedious, especially since most of my queries are far more complex, utilizing contain(). I imagine somewhere in CakePHP's core there's a place this kind of functionality could be added, I just don't know where.
Essentially, I'm looking for where CakePHP casts the database query to a php variable, so I can inject my additional model value.
I do know I will never use the column name "model" anywhere in my application. I'm also certain I want this information where I'm requesting it to be in every singe query, as little sense as it may make.
Add this to every model where you need it:
public function afterFind($results, $primary = false) {
foreach($results as $ikey => $item) {
foreach($item as $skey => $subitem) {
if(is_array($subitem))
$results[$ikey][$skey]['model'] = $skey;
else $results[$ikey]['model'] = $skey;
}
}
return $results;
}
Unfortunately I wasn't able to get this work when I stored it in AppModel.

Best way to store this data in a variable?

Some quick background info: I'm coding up a site which matches books to the classes they're required for.
I have two pieces of data that I need to represent in my code-- which books go with which classes, and the data (titles, authors, pricing, etc.) on these books.
Currently I represent this all with two arrays: $classArray, and $Books_data.
The advantage of this approach over a one-variable approach is that I don't repeat myself-- if a Book is required multiple times for different classes, only the ISBN needs to be stored in the $classArray and I can store the data in the $Books_array. This advantage is especially poignant because I have to query the pricing data from API's on the fly. If I only had a $classBooksArray, I'd have to loop the query responses into a big array, repeating myself (seemingly) unnecessarily.
The disadvantage of this approach is that these variables follow each other almost everywhere like Siamese twins. Nearly every function that needs one, needs the other. And my coding spidey sense tells me it might be unnecessary.
So, what would be the best way to store this data? Two arrays, one array, or some other approach I haven't mentioned (e.g. passing by reference).
Why not an associative which has two keys - one pointing to an array of classes, one to store Books
data?
$allData = array("classes" => &$classArray, "books" => &$Books_data);
That way you're only passing around 1 variable (less clutter) but retain all the benefits of separate data stores for books and classes.
Though, to be honest, if it's just TWO sets of data, so IMHO your spidey sense is wrong - passing both as separate parameters is perfectly fine. Once you get into a set of siamise sextuplet variables, then the above approach starts to actually bring benefits.
A multidimensional array.
$collection = array(
'classes' => array( /* Contents of $classArray */),
'books' => array( /* Contents of $Books_data */)
);
function some_function($collection) {
// looping over books
foreach ($collection['books'] as $book) {
// yadda yadda
}
}
Or better yet a class:
/* Define */
class Collection {
private $books;
private $classes;
public function __construct($classes = array(), $books = array()) {
$this->books = $books;
$this->classes = $classes;
}
public function addBook($book) {
$this->books[] = $book;
}
public function addClass($class) {
$this->classes[] = $class;
}
public function get_classes() {
return $this->classes;
}
public function get_books() {
return $this->books;
}
}
function some_function(Collection $col) {
// looping over books
foreach ($col->get_books as $book) {
// yadda yadda
}
}
/* Usage */
$collection = new Collection(); // you also could pass classes and books in the
// constructor.
$collection->addBook($book);
somefunction($collection);
If your datas were a database, your current proposal being a normal form would be canonical. The two variables would just become tables and ISBN a foreign key to books table (with a third table as a class has several books). I would probably stick with the current implementation as it will be very easy to transform to database when that will be necessary (and it usually happens faster than expected).
EDIT: a comment, say it is already in a database... what I do not understand is why you would want to store a full database in memory instead of just keeping what is necessary for the current task.
Let's be OO and put the arrays into an object. Define a class with those properties, load it up, and call its methods. Or, if you must have other functions operating with the object's data, pass the instance around. Disallow direct access to the data, but provide methods for extracting the salient info.
class book_class_association {
protected $books_to_classes = array();
protected $classes_to_books = array();
function __construct() {
$this->books_to_classes = array(
'mathbook1' => array('math'),
'mathbook2' => array('math'),
);
$this->classes_to_books = array(
'math' => array('mathbook1', 'mathbook2'),
);
}
function classes_for_book( $class_name ) {
return $this->books_to_classes[$class_name];
}
function books_for_class( $class_name ) {
return $this->classes_to_books[$class_name];
}
}
Is there a reason you are not storing this data in a database and then querying the database? It is a many to many relationship, and you would need 3 tables - class , book, and class_book_intersection.
So for example, you ui could have "select class" from a list, where the list is derived from the rows in class.
Then if the class id selected is 123. The query would then be something like:
Select book.title, book.cost
from book
inner join class_book_intersection
on
class_book_intersection.classid = 123 and
class_book_intersection.bookid = book.bookid

Categories