Laravel function take Model id vs function take Model itself - php

I want to know what should be the best approach to make function:
function get_total_payment($payment_id){
return PaymentModel::where('payment_id', $payment_id )->sum('amount');
}
function get_total_payment($payments){
return $payments->sum('amount');
}
what I think is the second approch is save us when refactoring code.
both work but I want to know the pros and cons

In my opinion, I think it's better to use eloquent scopes to make the total work on many different situations. see the examples below:
function scopeGetTotalPayment($query){
$query->sum('amount')
}
now you can Implement it for different situations. see the examples below:
//get the total amount of payments for the whole payments in the table
$totalPayment = PaymentModel::getTotalPayment();
//get the total payments for a specific user
$totalPayment = PaymentModel::where('user_id', $userId)->getTotalPayment();
In the end, it depends on you. you can either use the scopes or stick with the normal sum. I think that the added value of using the scope in your case is only for making the name clear and to make it work for a specific column.
read more about eloquent scopes: https://laravel.com/docs/9.x/eloquent#query-scopes

Related

OOP in conjunction with any API/SQL

Currently I am combining two APIs and as I have done in former such projects, I create a 1-1 relation between the SQL Table rows and fields in an object.
But I am unsure how to get around it when dealing with an API that takes some information of an item and from this information provides new information that's attached to that very item.
Example in PHP:
class Cart {
public $cartItems, $reference;
public function __construct(array $cartItems, string $reference) {
$this->cartItems = $cartItems;
$this->reference = $reference;
}
}
Say an API (Alike Imaterialize) took a json_encoded version of this and would return a cart_id.
Question: What would be the best approach at this point? - Should one add an extra field to the Cart class or would one create another class that extended Cart with this extra field?
If Cart had an extra field that would get initialized only after the information was available, then how would one go about differentiating a Cart that had the ID and one that had not? - Sure one could create a boolean or any sort of function to keep track of it.
I find it hard to get my head around, but my best guess would be that what I am searching is a Design Pattern for this kind of interaction.
Thanks in advance.

How can I sort a table of Laravel Eloquent objects by a custom attribute?

I have a Laravel 4.2 project (though I'll soon upgrade it to Laravel 5, so I'm interested in answers that apply to either version of Laravel). I often define custom attributes for models to make it easier to calculate and display certain values. (See the Laravel Documentation for how this works.) E.g.:
public function getFooBarAttribute() {
// Do some complex calculations, etc.
return $result;
}
Then in my view I can do {{ $object->foo_bar }} to print the result of that calculation.
Often such code will appear as a column of a <table> displaying a list of objects. I want to make the table sortable by that column. What is the best way to do that?
The main solution I have come up with so far is to convert any calculations in that function to equivalent SQL (and from there into Eloquent / QueryBuilder syntax). However, this is error-prone, makes the code harder to read, and is difficult to maintain (since it must be changed if the attribute function ever is).
Another solution would be to sort the resulting Collection manually, but I'm under the impression that doing so would be slower. (Is this true?)
How else can I accomplish this?
The only way to do this would be, as you stated, convert the calculations or code in your custom attributes into SQL. If you're already doing that you might as well retrieve your custom attribute with SQL as well. You could try to accomplish that with a scope: http://laravel.com/docs/4.2/eloquent#query-scopes
Lets consider an example:
You have a table products with a column price and tax. Lets say you have a custom attribute priceTotal as follows:
public function getPriceTotalAttribute() {
return ($this->price + $this->tax);
}
In this case you could define a scope to replace this attribute.
public function scopePriceTotal($query)
{
return $query->selectRaw($this->table . '.*, (price + tax) AS priceTotal')
->orderBy('priceTotal');
}
Of course this is a simple example, your code would probably be far more complex.
This is also more verbose, because it would require you to call this scope every time you need the custom attribute. But it's the only way to make SQL aware of your custom attribute and to be able to order your SQL result accordingly.
Your best bet would be using the using the collection to order your result. It's actually pretty fast, so I doubt it would slow down your application by much. The documentation shows how to order your collection: http://laravel.com/docs/4.2/eloquent#collections and here's another useful article on collections: http://codebyjeff.com/blog/2015/05/stupid-collection-tricks-in-laravel
You are describing sorting a collection. Here is a similar question. Calculation and sorting should be faster if done in SQL rather than PHP. To make it easier you could create some table views in your database and query them, instead of doing all the calculation each time in Laravel.

Cakephp + HttpSocket component + Restful API

Recently I am coding a small web page with CakePHP. The most of my models represent data that has to be collected through an external API with the HttpSocket component which provides CakePHP 2.x.
This is the main reason I include the following method in AppModel.php
public function get ($url, $options = array()) {
$options += $this->default;
$url .= 'api_key=' . $options['api_key'];
$hs = new HttpSocket();
return $hs->get($url);
}
I want to implement some measures to control the number of times CakePHP call the API, because it has limitations (100 request per minute and 1000 per hour, for example)
What is the best way to accomplish this?
I thought about store data related to the request in a new table but I am not sure if I can do queries from AppModel.php
Thank you
I would create a new table like api_call_log and increment the count. You can simply get a model instance in your AppModel:
$ApiCallLog = ClassRegistry::init('ApiCallLog')
$ApiCallLog->log();
Put your logic for the count into the model method and pass arguments as you need, not sure what else you might need.
Instead of putting it in AppModel a behavior might be a better place and you can implement additional methods there as well as your code grows and attach it only to models that need it.
An alternative that is very likely less resource hungry than using a SQL DB is to use a Redis cache. So use Cache::read() and Cache::write() to get the actual count and increment it. This is very likely the better alternative if you get A LOT requests to the API. You might get collisions using a SQL DB if you don't lock the tables during the counting and incrementing operation. I never had this case but I would not count on that it won't happen.
Additional note: get() is a very generic name, I would rename it to httpGet(). Names that are not specific can be pretty confusing, especially if there are methods of the same name in other classes around.

PHP code duplication. At what point is duplicating code the right way to go?

I am using CodeIgniter but this question applies in a general sense too.
I have a table of transactions with columns
item_name | type | date | price | document
I want to do the following in two completely independent cases.
1) Get a list of transactions within a certain date range.
2) Get the total price of each transaction.type within a certain date range.
The former can be achieved by simply using a select statement with > datetimestamp
The latter can be achieved by selecting the SUM, and grouping by the type whilst like implementing any required where conditionals e.g with > datetimestamp
Although a simple case, to achieve this I need to have two methods however the bulk of both of these methods (namely the WHERE clauses) are duplicated across both methods.
In terms of speed etc it does not matter but it seems like pointless code reproduction.
A second example is as follows.
I previously had a method get_data($ID) which would get a row from a table based on the ID passed in.
As such in a separate method I would get my 100 items for example.. return an array, loop through them and call get_data for each.
This setup meant that many different methods could get different lists from different sources and then still use the same get_data function and a loop to get the required data.
This minimized code duplication but was incredibly ineffiecient as it meant looping through loads of data items and hundreds of db queries.
In my current setup i just join the data table in each of my methods - code duplication but clear improved efficiency.
A final example is as follows
In codeigniter I can have a function such as the following:
get_thing($ID)
{
$this->load->database();
$this->db->where('ID',$ID);
$this->db->get('table');
}
BUT in alternate situations i might want to only get items in a specific folder.. as such making the function more generic works better.. e.g.
get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
but then I might want to use this function in two different contexts e.g a user page and an admin page whereby admins can see all items, even unverified ones. My code now becomes:
get_thing($array,$show_unverified = false)
{
$this->load->database();
$this->db->where($array);
if($show_unverified == false)
{
$this->db->where('verified','YES');
}
$this->db->get('table');
}
As you can probably see this can quickly get out of hand and methods can become overly complex, confusing and full of conditionals.
My question is as follows - What are best practices for minimizing code duplication, and how could they be applied to the above situations? I spent hours and hours trying to make my code more efficient yet I'm getting nowhere because I cant workout what I should really be trying to achieve.
Cheers
My idea on code duplication in database access functions is that it is often better to keep it separate.
My rule here is especially that a function should not return different kinds of data depending on the parameter, for example it should not return a single user sometimes and other times an array of users. It may return error codes (false) though.
It is ok though if the function implements different access levels, which are shared across several pages.
This basically always comes back to common sense. You should try to minimize duplicate code and try to reduce complexity within single function. Keep them small and simple.
So basically everytime you try to generalize a function like this you would have to ask if the problem of duplicate code is bigger than the problem of overly complex functions.
In this case i would stop at your second point and next you could create some wrappers for the most common tasks (but be carefull not to make a maze of wrappers)
//you generic function
function get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
// a nice and friendly wrapper
function get_thing_by_id($id)
{
get_thing(array('id' => $id));
}
// this is just getting silly. don't go crazy with wrappers, only for very often used things.
// and yes the function name is purposely crazy ;)
function get_thing_verified_by_name_and_city_and_some_more($name, $city, $somethingElse)
{
get_thing(array('name' => $name, 'city' => $city, 'somethingelse' => $somethingElse));
}
This answers the first part of your question. Assuming you're using mysql_fetch_assoc or similar. As you're iterating over the result sets you could store count values in a variable in the loop for the total price of each transaction type.
The second part as long as you're not repeating code ad infinitum which would cause you issues down the line when maintaining the code base, it's OK. For your function you could always test the type of variable being passed to the function and set conditional behaviours accordingly.
Have a look at the factory pattern or strategy pattern relating to software design patterns for further insight.

Using Magento's addFieldToFilter to filter by existing columns

Hypothetical situation. I want to populate/load a sales/order collection with every order where the grand_total is equal to the total_paid. I know I can use addFieldToFilter to filter by a specific value, but is it possible to use this method to filter by other database values. If not, is there any data access object in the Magento system that allows this.
$orders = Mage::getModel('sales/order');
$orders = $orders->getCollection();
$orders->addFieldToFilter('total_paid',Array('eq'=>30)); //would find all the orders that were 30
//syntax to find all the orders whose total_paid value is equal to it's grand_total attribute
//????????
The non-eav SQL concept I’m grasping for is:
SELECT * FROM Orders o WHERE o.total_paid = o.grand_total
Is this possible to do purely with object method calls, or do I need to do post-load filtering?
How do other ORM systems handle this?
I was waiting for a definite answer to this, but seeing as nothing's come up I might as well share what I've found out.
I traced addFieldToFilter and apparently every value you use ends up quoted so you can't really "hack" this by using the name of a column.
In fact I don't see any way to do this, at least not within this class.
Realistically I think you'll need to go with post-load filtering, at least for now. This of course is the very definition of inefficient code, but then again if computational efficiency was a priority, you wouldn't be using Magento in the first place...
Just had to address this in one of my projects and came across this question while searching for a better resolution. My hack looks like this:
$orders = Mage::getModel('sales/order')->getCollection();
$orders->getSelect()
->where('total_paid=grand_total');
It annoys me that such a basic functionality of databases is overlooked by Magento's ORM.
There's a "polite" workaround for that.
See my example below where I am trying to filter where created_at <= expire_at.
Mage::getSingleton('foo/bar')->getCollection()
->addExpressionFieldToSelect('expired', '(created_at >= expire_at)', ['created_at' , 'expire_at'])
->addFilter('(created_at >= expire_at)', 0)
It's stupid, but still solves the problem using the class.
PS: I hope that 11 years after publishing this question you've solved the problem. :D (Just a joke. I love your work.)

Categories