How to Select Fields When Refreshing Model in Laravel - php

As you probably know, we can use refresh method on a model instance in order to refresh it from the database.
Refresh completely reloads all columns from database. refresh method does not seem to take any input, so I was wondering if there is a way to refresh some fields from the database.
To demonstrate, let's have a pseudo-model as below:
Foo
---
bar: char
baz: char
And I have a single instance of Foo called $foo. $foo->refresh() fetches both $foo->bar and $foo->baz. How do I only fetch/refresh bar field?
Thanks in advance.
Environment
PHP 7.4.5
Laravel 7.x

I think there's no way to do such. The best option you have is to get the PK of your $foo object and query for the fields you want:
$id = $foo->id;
$refreshed = Foo::select('bar')->where('id', '=', $id)->first();
$foo->bar = $refreshed->bar;
However, this will unsync your model with the database. Another option is to create a custom method in your model that does it in a more abstract way and retrieves the new unsynced object:
// In your model:
public function refreshField(string $field)
{
$this->$field = (new static())
->select($field)
->where($this->key(), '=', $this->{$this->key()})
->first()->$field;
return $this;
}
// Then outside:
$foo = Foo::first();
// perform actions. . .
$foo->refreshField('bar');
Again, keep in mind that this will keep your model unsynced with the database. Also, this is a POC, I didn't test it, but the general idea should work. If you create a parent model and extend all your other models from this (which extends from Eloquent), this should work with any of your models.
For a more complete version, you can pass an array instead of a string and retrieve all the fields that you want to refresh, then assign them in a loop:
public function refresFields(string ...$fields)
{
$fields = $this->$field = (new static())
->select($fields)
->where($this->key(), '=', $this->{$this->key()})
->first();
foreach ($fields as $field) {
$this->$field = $field;
}
return $this;
}
I wouldn't recommend this, but if you really need it, give it a try. Good luck

Related

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

How do I filter item(product) with multiple parameter in MongoDB / PHP?

I am using MongoDB with Laravel (php framework).
Using post method I am sending filter parameter to controller function.
ItemController.php
public function search_item(Request $request){
$item = $request->all();
$item = $this->item->search_item($item);
return response()->json($item,200);
}
Item.php (model file)
public function search_item($item){
$items = new item();
foreach($item as $key => $value) {
$items = $items::where($key, '=', $value)->get();
}
$items = json_decode(json_encode($pro));
return $items;
}
If I pass only one parameter then it's give me result perfectly but if I pass more then one parameter it's give me an error.
Non-static method Illuminate\Support\Collection::where() should not be called statically
Let me explain with example :
URL : http://localhost/use_good_store/public/search-item?brand_name=Bajaj
If I post above url it's give me perfect result but if I pass more then one parameter like
http://localhost/use_good_store/public/search-item?brand_name=Bajaj&model=Boxer it gives me above error.
There are a couple of things I would change in this code.
First, to address the error you're getting, you're accessing your search method incorrectly. It's an instance method, not a class (or static) method.
So instead of $this->item::search_items($item), it should be $this->item->search_items($item).
But I wouldn't recommend that. Since you're using the model anyway, go ahead and store a static search method on it for future use. It doesn't make much sense for it to be an instance method; you can't really call it on an instance because the point of it is to find many other instances.
Additionally, your query probably isn't going to work out the way you want since you're continually replacing the $items value in the for loop. if you're going to be using the QueryBuilder, you can just keep adding where() clauses to it all day long.
For this, I'd recommend adding a static searchItems() method that returns the results of a query which you would then convert to JSON in your controller, like so:
//Item.php model
public static function searchItems($itemSearchAttributes)
{
return static::where(function($query) use ($itemSearchAttributes){
foreach ($itemSearchAttributes as $key => $value){
$query->where($key, '=', $value);
}
})->get();
}
//ItemController.php handler method
public function search_item(Request $request){
$items = Item::searchItems($request->all());
return $response->json($items);
}
Worth noting; your search method here will exclude any records that do not match -all- of the provided key/values.
Try to make it like this :
$items = new item();
foreach($item as $key => $value){
$items = $items->where($key, '=', $value);
}
$items = $items->get();

Getting data from database to model

So far I've used many objects in my applications but often if I had to for example display for example users' profiles on page I simply got 20 users from database as array using some method in my object and assigned it to view.
Now I want to create application more with models that represent real data. So for each user I should probably have User object with properties .
Here I put sample code to get users from database and to display them in PHP:
<?php
$db = new mysqli('localhost', 'root', '', 'mytest');
class User
{
private $id;
private $name;
public function __construct($data)
{
foreach ($data as $k => $v) {
if (property_exists($this, $k)) {
$this->$k = $v;
}
}
}
public function show()
{
return $this->id . ' ' . $this->name ;
}
}
$result = $db->query("SELECT * FROM `user` LIMIT 20");
$users = array();
while ($data = $result->fetch_object()) {
$data->x = 10; // just to test if property isn't created
$users[] = new User($data);
}
// displaying it on page
foreach ($users as $user) {
echo $user->show() . "<br />";
}
Questions:
Is it the way I should use data from database into model? I mean if should I create object for each record even if the only role of this object would be returning some data to view (for example even not modified by any functions as in this example). Of course I know that often data should be prepared to display or made some calculations or additional data should be retrieved from database before those data could be used to display.
Is there any way to assign object properties from database simpler than using constructor with loop?
First of all, i'd move the DB operations in a separate class, not inline with the User class. You could create an abstract Model class, which the User class would extend and add DB logic to it.
You'd have a select() method to query the database, which would return an array of objects of the class that extended the Model class (the User class in this case).
To answer your questions:
I think it's ok. Most ORMs work this way.
An alternative would be to assign the user row data from the DB to a $data attribute in your User class and use the magic methods __get and __set to access them.

Laravel 4: How to apply a WHERE condition to all queries of an Eloquent class?

I'm trying to implement an "approved' state for a table I have, it's pretty straightforward, basically, if the row's approve column equals 1; that row should be retrieved, otherwise it shouldn't.
The problem is, now I have to go through the whole codebase and add a WHERE statement(i.e., function call) which is not only time consuming but also inefficient(if I ever want to remove that feature, etc.)
How can I do that? Is it as easy as adding $this->where(..) inside the Eloquent child class' constructor? Wouldn't that affect other CRUD operations? such as not updating an unapproved row?
The answer was given when there was no query scope feature available.
You can override the main query, only for the Post model, like
class Post extends Eloquent
{
protected static $_allowUnapprovedPosts = false;
public function newQuery()
{
$query = parent::newQuery();
if (!static::$_allowUnapprovedPosts) {
$query->where('approved', '=', 1);
} else {
static::$_allowUnapprovedPosts = false;
}
return $query;
}
// call this if you need unapproved posts as well
public static function allowUnapprovedPosts()
{
static::$_allowUnapprovedPosts = true;
return new static;
}
}
Now, simply use anything, but unapproved users won't appear in the result.
$approvedPosts = Post::where('title', 'like', '%Hello%');
Now, if you need to retrieve all posts even unapproved ones then you can use
$approvedPosts = Post::allowUnapprovedPosts()->where('title', 'like', '%Hello%');
Update (Using the query scope):
Since, Laravel now provides Global Query Scopes, leverage that instead of this hacky solution, notice the date of this answer, it's too old and so much things changed by now.
// Using a local query scope
class Post extends Eloquent
{
public function scopeApproved($query)
{
return $query->where('approved', 1);
}
}
You can use it like:
$approvedPosts = Post::approved()->get();
The closest thing I found is Eloquent query scope.
Even though it requires a minor change in my code(prefixing queries) it still gives me what I'm looking with great flexibility.
Here's an example:
Create a function within the Eloquent child class:
class Post extends Eloquent {
public function scopeApproved($query)
{
return $query->where('approved', '=', 1/*true*/);
}
}
Then simply use it like this:
$approvedPosts = Post::approved()-><whatever_queries_you_have_here>;
Works perfectly. No ugly repeated WHERE function calls. easy to modify. Much easier to read(approved() makes much more sense than where('approved', '=', 1) )
You can use global scope for your need, docs for that are here : https://laravel.com/docs/5.6/eloquent#query-scopes
Good example is SoftDeletingScope which is applied to all queries by default on models which use SoftDeletes trait.

Doctrine: Set model values as array

I've got an array of values I want to update my model with.
Doctrine_Access provides a function setArray which is nearly exactly what I need - except it cares about values that don't have fields in the model. I want those to be ignored.
A little example. Say we have a User table with the field username.
$user = new User();
$user->setArray(array('username'=>'xyz'))->save();
That would work!
$user = new User();
$user->setArray(array('username'=>'xyz','anotherKey'=>'anotherValue'))->save();
That doesn't. I want Doctrine to just ignore anotherKey, if there is no related field.
The intention is, that I don't want to filter my arrays before I update my model.
What is the cleanest and easiest way to get this done?
Doctrine_Record::fromArray() solves it.
Unfortunately it doesn't return the object, so it's useless for method chaining...
this is useful
add find method to model:
class Address extends Doctrine_Record {
public static function factory() {
return new Address();
}
public function findById($id) {
$findObject = Doctrine::getTable('Address')->findOneByid($id);
return $findObject;
}
....
and use it
$address = Address::factory()
->findById(13)->set('name', 'new data')->set('anotherfield','another data')->save();

Categories