I have several fields in some of my database tables that my CakePHP models never need to retrieve. Is there some way to set a default set of fields to fetch at the model level? For instance I retrieve some data from a third party designed database that has 50 fields per table, I use 5.
I know I can set limits on fields at the time of the find() query and at the time of any associations between models, but I was wondering if there was a model-level approach.
CakePHP does not offer what you describe at the Model level out of the box. That is to say there is no Model property of defaultFields that is used on every find()
As you noted, you could specify this at the association level by setting the fields property. However, this would only work when you were retrieving the Model across one of these relationships.
In the end, you're going to be setting this in your find(). You could minimize repeating yourself by adding a property to your model like so:
var $defaultFields = array('Model.field1', 'Model.field2', ...);
Then in your find():
$this->Model->find('fields' => $this->Model->defaultFields, ...);
This has obvious limitations, but at least provides some encapsulation and therefore flexibility.
Note: A more invasive approach could use beforeFind();. In which case you would not need to adjust every find(). But your mileage may vary based on your usage.
Related
Ok so i'm kind of newish to eloquent and laravel (not frameworks tho) but i hit a wall here.
I need to perform some queries with conditions on different tables, so the eager load (::with()) is useless as it creates multiples queries.
Fine, let use the join. But in that case, it seems that Laravel/Eloquent just drops the concept of Object-relationship and just return a flat row.
By exemple:
if i set something like
$allInvoicesQuery = Invoice::join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();
and then looping such as
foreach ($allInvoicesQuery as $oneInvoice) {
... working with fields
}
There is no more concept of $oneInvoice->invoiceFieldName and $oneInvoice->contact->contactFieldName
I have to get the contacts fields directly by $oneInvoice->contactFieldName
On top of that the same named columns will be overwrited (such as id or created_at).
So my questions are:
Am i right assuming there is no solution to this and i must define manually the field in a select to avoid the same name overwritting like
Invoice::select('invoices.created_at as invoice.create, contacts.created_at as contact_create)
In case of multiple joins, it makes the all query building process long and complex. But mainly, it just ruins all the Model relationship work that a framework should brings no?
Is there any more Model relationship oriented solution to work with laravel or within the Eloquent ORM?
Instead of performing this join, you can use Eloquent's relationships in order to achieve this.
In your Invoice model it would be:
public function contact(){
return $this->belongsTo('\App\Contact');
}
And then of course inside of your Contact model:
public function invoices(){
return $this->hasMany('\App\Invoice');
}
If you want to make sure all queries always have these active, then you'd want the following in your models:
protected $with = ['Invoice']
protected $with = ['Contact'];
Finally, with our relationships well defined, we can do the following:
$invoices = Invoice::all();
And then you can do:
foreach($invoices as $invoice)[
$invoice->contact->name;
$invoice->contact->phone;
//etc
}
Which is what I believe you are looking for.
Furthermore, you can find all this and much more in The Eloquent ORM Guide on Laravel's site.
Maybe a bit old, but I've been in the same situation before.
At least in Laravel 5.2 (and up, presumably), the Eloquent relationships that you have defined should still exist. The objects that are returned should be Invoice objects in your case, you could check by dd($allInvoiceQuery); and see what the objects are in the collection. If they are Invoice objects (and you haven't done ->toArray() or something), you can treat them as such.
To force only having the properties in those objects that are related to the Invoice object you can select them with a wildcard: $allInvoicesQuery = Invoice::select('invoices.*')->join('contacts', 'contacts.id', '=', 'invoices.contact_id')->get();, assuming your corresponding table is called invoices.
Hope this helps.
I have a many-to-many relationship where the pivot table has about 20 additional columns. I am using a custom pivot class, and I have successfully set up the code to return an instance of that class when the ->pivot property is accessed on the relation, e.g.
$supplier->products->pivot returns the custom pivot class.
However, when wanting to access the data, I can manually define all the individual attributes of the pivot class (which extends Pivot by the way) in the belongsToMany relationship like this:
return $this->belongsToMany(Product::class, ['prop1', 'prop2', 'prop3'])
...But, how can I retrieve all the pivot data of the class without manually defining them as it ties the relationship declaration very close to the class? Is this possible. If not, it's going to make maintainability a PITA! Ideally, it'd be really nice if withPivot just had a flag to get it all!
In my circumstances, I found it easier to separate all the data into a separate table and model, and add a foreign key in the pivot table to the additional table record. This allows me to use the 'normal' model handling in Laravel and means I don't have to mess around with problems like this!
My use case was a schema of product and supplier with a many-to-many, and each supplier having their own data for the product, namely price, stock, shipping cost / times etc, so I moved all this from the pivot to a SupplierProduct model.
I'll leave this question here, as although this isn't the direct answer to the question (which I fear the answer is 'no'), this is a solution which is viable and can save quite a bit of coding frustration!
I have a set of 'clients' and they are displayed in a GridView. 'Clients' can not be deleted by the average user, only marked for deletion. However I would like it so they are not displayed in the list once they are marked for deletion.
I am using Yii2, and the soft deletion table contains has three columns that concern the data 'foreign_table', 'foreign_column' and 'foreign_value'. It is set up like so as I do not want to delete data from the 'Clients' table unless it is needed.
How would I go about filtering the results of the GridView?
Would I use $model->search?
I believe you could use $model->search to filter the result, provided that the following steps are already done:
You have a means of flagging rows marked for deletion: state or status.
In the search() method of the Model represented by $model you specify the criteria used to filter the result by using: $criteria->condition = 'status=1 OR status=2';.
Depending on your database and how you have decided to flag or marked-as-deleted, you may have to modify $criteria. You may even chose to use $criteria->with to include foreign tables.
So you have a table where you save references to items that should be considered as deleted.
I think it could be reasonable to override find() of the Client class since there could be many cases where you want to ignore the marked-as-deleted clients. If so you could write the appropiate query in find(). As a nice side effect the GridView will show only the not marked-as-deleted clients automatically.
To get the marked-as-deleted clients you would need an extra methode, of course, if this is needed at all (you could want to delete them in the db directly and you don't need php code for this). But most of the time you don't need them. So you could filter them out by overriding the find method.
Recently started working with OOP in PHP. Following the "code to an Interface" principle, i got confused as to the type hint to use when passing a single object or multiple as argument to a method.
Currently, i have a "Student" class - represents a row in my students table, i also have a "Students" class that holds multiple student objects in an array.
To fetch the profile of one student, i pass the Students object (holding a single student object) to the profile class. I set a Students type hint in the profile class.
Now i feel this is bad code as i have lines like this
student = new Students();
and students = new Students();
question is,
am i on the right path?
if i remove the Students class and work with Student alone, based on the principle, how do i pass multiple Student objects (assuming array) to the profile class if it accepts a Student type hint?
what options do i have?
Thanks.
If by Students you mean a collection of Student objects, perhaps a better name would be StudentCollection or StudentSet.
There are two ways around the type hint problem:
Introduce a method on StudentCollection called ->getProfiles(); it would return an array of profiles for each Student instance it's managing by calling methods on Profile.
Introduce a (static) method on Profile that operates on a StudentCollection instance.
The first option has feature envy, which is why I've included a workaround.
Instead of reinventing the wheel you might want to try Doctrine or at least take a look at its architecture.
I'm not sure if I get your exact issue... But if you want to go for your own code I would first abstract the DB layer as well and have some base classes like Database, Table, Row, Field that an describe the DB stack and extend them as needed with some magic methods. So when you do Student extends Table it would automatically check for a "students" table or whatever else convention you like to implement. Alternatively you could just pass the table name as arg.
Whatever Object is returning the result set from the database would have to construct a single Row object for each row and add it to a collection of rows that I would name ResultSet and contains all the row objects and return that collection.
I have a bunch of DNS records in a table, where the "type" column defines the type of the given record.
I would like to have a class hierarchy of models in Lithium representing these records such as:
RR - base resource record (abstract - sort of)
RR_SOA - SOA record, extends RR
RR_CNAME - CNAME record, extends RR
... etc ...
So one class for each record, all mapping to the same table.
Next I would like to use RR::find which would automagically give me a list of objects where the class of each object corresponds to the type of the actual record.
Is this even possible without too much black magic?
Yes (anything is possible) but you're approach may be too ambitious. In my POV, I would depend on #class RR to handle the basic querying of the target table, but by utilizing filters on RR (specifically on the find method inherited from Model), you can pass-by-reference the instance to sets of newly instantiated classes (SOA, CNAME, etc...) along with their position in the table (so SOA object is only associated with that specific record or primary key).
There is some black magic going on, but nothing the Lithium core developers didn't account for.
Bottom line, 1 base class for your table (RR model), multiple (possibly inherited from another base unrelated to Model) for SOA, CNAME, etc..., and a filter (put anywhere really) to intervene RR::find and/or the late binded RR::findby*
Does this make sense?
(This will require some trial and error. First to see if you can manipulate the data output, after that the rest is butter)