I have mapped my custom table with Magento, so that i can fetch all data in it using below
Mage::getModel('custom/filter')->getCollection();
Below is the sample table i have mapped
filter_id filters
------------------
1 5,6,5
3 77,8,5,77
10 22,55,33
I need to count the number of occurence of a specific string under field filters in this table using collection.
say for example, if i want to count occurence of 77 in 2nd row (filter_id = 3).
How do i do it with model collection?
I know i can use query to directly query the database using query methods in magento, but i am trying to do this in collection way.
Any suggestions would be helpful.
Thanks,
Balan
If I am understanding this correctly, the way that I would go about it would be to put your logic into the model. Simply put, you will make your selection like so:
$filter_id = 3;
$query = 77;
$collection = Mage::getModel('custom/filter')->getCollection();
$collection->addFieldToFilter('filter_id', $filter_id); // Hopefully it will be more than just one
foreach($collection as $filterModel) {
$filterModel->getOccurrences($query);
}
/////****** Company/Custom/Model/Filter.php ******////////
<?php
class Company_Custom_Model_Filter extends Mage_Core_Model_Abstract
{
protected function _construct()
{
$this->_init('custom/filter');
}
public function getOccurrences ($value)
{
$exploded = explode(',', $this->getFilters());
$count = 0;
foreach($exploded as $explodedValue) {
if ($explodedValue === $value) $count++;
}
return $count;
}
}
And, if you wanted to refactor this code into a collection, it will be pretty much the same iterator, going through each model. It is just a matter of where the initial code is called from.
Related
I need to create Models based on the alphabet as their tables names are like 'products_a', 'products_b'....'products_z'. But I don't want to create all these models as separated files,
but want to find a way to use all these tables dynamically.
And all tables are separated based on some 'shopping-mall id' values.
I've found a solution to set tables dynamically like this solution.
So what I tried before was like below.
class Products extends Model
{
use BindsDynamically;
public $timestamps = false;
public $fillable = [ 'product_name', 'reg_time'];
public static function newProduct($mall_id)
{
$rangeArr = range('a', 'z');
$product = new Products;
foreach ($rangeArr as $ar) {
if (strtolower(substr($mall_id, 0, 1)) == $ar) {
$product->setTable('product_'.$ar);
}
}
return $product;
}
}
However, now I created a Cart model and tried to use the relationship methods with all the separated tables which I cannot.
So I realized I need the separated models with relationships not just tables.
Theoretically, my database has those tables.
products_a
products_b
....
products_z
products_cart
So, the products_cart table needs to have all the products tables idx data.
I want to use relationships like 'hasMany' or 'belongsTo', therefore I need all the separated models.
Can I make all the alphabetical models dynamically and use relationship methods?
If so, how can I do this?
OK, I understand we have a nasty DB design here. But then, we don't have a DB expert and I cannot figure what can I do about this DB design.
So, please don't judge about the DB design, rather guide the better way to replace it.
Maybe you UNION all product tables and overwrite the models newModelQuery() method to achieve that
class Products extends Model
{
use BindsDynamically;
public $timestamps = false;
public $fillable = [ 'product_name', 'reg_time'];
/**
* Get a new query builder that doesn't have any global scopes or eager loading.
*
* #return \Illuminate\Database\Eloquent\Builder|static
*/
public function newModelQuery()
{
$query = parent::newModelQuery();
$rangeArr = range('a', 'z');
foreach ($rangeArr as $ar) {
//if (strtolower(substr($mall_id, 0, 1)) == $ar) {
$query->union('product_'.$ar);
//}
}
return $query;
}
}
But that's probably just one of a couple methods you'd have to overwrite this way. And this solution currently doesn't support your $mall_id. But after all, it's due to bad database design.
We have two Models:
SimpleModel (id, country, code)
ComplexRelatedModel (id, name, address)
SimpleModel has many ComplexRelatedModel, then
class Product extends Model
{
protected $fillable = [
'name'
];
/* hasOne */
public function complexRelatedChild()
{
return $this->hasOne(self::class, 'parent_id', 'id');
}
}
If we do
$simples = SimpleModel
->with('complexRelatedChild')
->simplePaginate(100000 /* a lot! */);
And we need only do
foreach ($simples as $simple) {
echo $simple->complexRelatedChild->name;
}
Any ComplexChild has hydratated and ready. This takes a lot of memory in my case. And we need just one field without any funciton or feature of Model.
It's possible use some data field from related object or with eloquent this isn't possible?
Not sure I completely understand your question. You want to only load one field from the complexRelatedChild relation to keep memory limit down?
You could do:
$simples = SimpleModel::with(['complexRelatedChild' => function($query){
return $query->select(['id', 'name']);
})
->simplePaginate(100000);
Which can be simplified to:
$simples = SimpleModel::with('complexRelatedChild:id,name')
->simplePaginate(100000);
However if I were you, I would try to paginate less items than 100000.
Update:
You could use chunk or cursor functions to process small batches of SimpleModel and keep memory limit down.
SimpleModel::chunk(200, function ($simples) {
foreach ($simples as $simple) {
}
});
or
foreach (SimpleModel::cursor() as $simple) {
}
See the documentation for more information
I am trying to setup Eloquent for a new API that we're working on. I am using relations in a model.
Some relations are complex and aren't really suitable for a quick chained Query Builder statement. For example, I am trying to return metrics and some of those metrics are complex. Such as counting the total clicks generated by a user (it's not just a simple COUNT(*)). Here is the code that I have now:
<?php
namespace App\Models\Eloquent;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
class Affiliate extends Model
{
protected $table = 'user';
public function profile()
{
return $this->belongsTo('App\Models\Eloquent\Profile', 'id');
}
public static function clicks()
{
$sql = "SELECT
user_id,
COUNT(*) / SUM(dummy_metric) AS total_clicks
FROM clicks
WHERE something = 'true'
AND another_thing > 100 # dummy filter for example only
GROUP BY user_id";
$rows = DB::select(DB::raw($sql));
// (PSUEDO CODE) THIS IS WHAT I'D LIKE TO DO IDEALY
return $this->belongsTo($rows, user_id);
}
Possible? I'd like to be able to write our own queries without relying on Query Builder all of the time, but I still want to be able to join the relation to Eloquent.
Assuming you have Clicks model defined, find below the eloquent version of your query.
Click::select(DB:raw("user_id, COUNT(*) / SUM(dummy_metric) AS total_clicks"))
->where(/*condition*/)->groupBy("user_id")->get();
Note:
1) Where method accepts an array of conditions, so you can add more than one condition in same method. Reference
Update
I think thius should be your clicks() method:
public function clicks(){
return $this->hasMany('App\Models\Eloquent\Click',/*relevant ids*/);
}
And, now where you want a count of clicks(in controller for example), you can use following query:
$user = User::find("user_id")->with('clicks')->get();
$clicks = $user->clicks()->count();
To make it more efficient, refer to this article on Tweaking Eloquent relations
Update 2:
You can use Accessor function to retrieve total count
Add following 2 methods in User model.(change clicksCount string to anything you need )
public function clicksCount(){
return $this->hasOne('App\Models\Eloquent\Click')
->select(DB:raw("user_id, COUNT(*) count"))
->groupBy("user_id");
}
public function getClicksCountAttribute(){
return $this->clicksCount->count();
}
Now, you can use $user->clicksCount; to get the count directly.
If you are using Laravel 5.4, you can use withCount() method to easily retrieve the count. Counting Related Models
What is the best way to do this listing?
I would not want to do it that way "ugly".
/**
* Get user indicateds
* #return array|null
*/
static public function indicateds()
{
$users = ModelUser::all();
foreach( $users as $user ) {
if( $user->financial->status_payment ) {
$newArray[] = $user;
}
}
return (isset($newArray) ? $newArray : null);
}
Thanks
You can use the collection's filter method:
return ModelUser::with('financial')
->get()
->filter(function($user) {
return $user->financial->status_payment;
});
I'm supposing you have defined the financial relation and you should eager load it as I did to improve the performance.
One of the benefits to relationships is that you can use them to modify your queries, as well. So, instead of getting all users into a Collection, and then filtering that Collection, you can use the relationship to modify the query so that you only get the desired records in the first place. This will reduce the number of records returned from the database, as well as the number of model instances that get created. This will save you time and memory.
$users = ModelUser::with('financial')
->whereHas('financial', function($q) {
// $q is the query for the financial relationship;
return $q->where('status_payment', true);
}
->get();
The with() is not required, but if you'll be accessing the financial relationship on the returned users, it is a good idea to eager load it.
The whereHas() is where the magic happens. It modifies the query so that it will only return users that have a related financial record that matches the conditions added by the closure used in the second parameter.
You can read more about it in the documentation here.
Is it possible to return an value for an hasOne relation directly with an Model?
For example:
$List = Element::orderBy('title')->get();
The Element has a "hasOne" Relation to an column:
public function type()
{
return $this->hasOne('App\Type', 'id', 'type_id');
}
How can i now return automatically the "type" for the Model?
At the Moment i am looping through all Elements, and build my own "Array" of Objects, including the Key of "type" in this example. But ill prefer to do this only in my Model.
Ill know how to add a "normal" property, but can it be someone from an relation?
public function getTypeAttribute($value)
{
return // how ?
}
protected $appends = array('type');
Is this possible?
Edit:
A workaround could be to use DB:: to return the correct value - but ill dont thing thats a good workaround: like:
public function getTypeAttribute($value)
{
// make a query with $this->type_id and return the value of the type_name
}
protected $appends = array('type');
you need to eager load your relations when getting the Element:
$list = Element::with('type')->orderBy('title')->get();
then access the type using
foreach ($list as $item) {
echo $item->type->type_name;
}
where type_name would be the name of a column in the types table
Make a query scope and in that scope, join your attributes from other tables.
http://laravel.com/docs/5.1/eloquent#query-scopes
http://laravel.com/docs/5.1/queries#joins