I am not sure how to increment the value in a column using Eloquent Model in Laravel 4?
This is what I currently have and I am not sure how correct is this.
$visitor = Visitor::where('token','=','sometoken')->first();
if(isset($visitor)){
$visitor->increment('totalvisits');
}else{
Visitor::create(array(
'token'=>'sometoken',
'totalvisits'=>0
));
}
With Query Builder we could do it using
DB::table('visitors')->increment('totalvisits');
Looks like the code that I posted worked after all
$visitor = Visitor::where('token','=','sometoken')->first();
if(isset($visitor)){
$visitor->increment('totalvisits');
}else{
Visitor::create(array(
'token'=>'sometoken',
'totalvisits'=>0
));
}
Prior to a fix a few weeks ago the increment method actually fell through to the query builder and would be called on the entire table, which was undesirable.
Now calling increment or decrement on a model instance will perform the operation only on that model instance.
Laravel 5 now has atomic increment:
public function increment($column, $amount = 1, array $extra = [])
{
if (! is_numeric($amount)) {
throw new InvalidArgumentException('Non-numeric value passed to increment method.');
}
$wrapped = $this->grammar->wrap($column);
$columns = array_merge([$column => $this->raw("$wrapped + $amount")], $extra);
return $this->update($columns);
}
which essentially works like:
Customer::query()
->where('id', $customer_id)
->update([
'loyalty_points' => DB::raw('loyalty_points + 1')
]);
Below is old answer for Laravel 4 where the built-in increment was a seperate select and then update which of course leads to bugs with multiple users:
If you'd like to accurately count your visitors by ensuring the update is atomic then try putting this in your Visitor model:
public function incrementTotalVisits(){
// increment regardless of the current value in this model.
$this->where('id', $this->id)->update(['totalVisits' => DB::raw('last_insert_id(totalVisits + 1)')]);
//update this model incase we would like to use it.
$this->totalVisits = DB::getPdo()->lastInsertId();
//remove from dirty list to prevent any saves overwriting the newer database value.
$this->syncOriginalAttribute('totalVisits');
//return it because why not
return $this->totalVisits;
}
I'm using it for a change tag system but might work for your needs too.
Does anyone know what to replace the "$this->where('id',$this->id)" with because since dealing with $this Visitor it should be redundant.
Related
Here is the code in which I get the user if it's g10_eng_unwh is equal to 1.
public function purchased(User $user)
{
if ($user->g10_eng_unwh == 1) {
$token = "g10_eng_unwh";
}
if ($user->g7_eng_unwh == 1) {
$token = "g7_eng_unwh";
}
$time = Carbon::now()->format('Y-m-d');
dd($user->email, $time, $token);
}
Instead, I want to get the user only when it's g10_eng_unwh value changes from 0 to 1.
Is there any way?
You need to get the past value at the beginning so that you can apply the condition, it will have to be stored somewhere. Most likely you will need to create g10_eng_unwh_old or something like that. I assume that g10_eng_unwh is a column in the database, in this case create g10_eng_unwh_old and store the old value in it.
In this case, you could get it even on request. For example
DB::query()
->where('g10_eng_unwh', 1)
->where('g10_eng_unwh_old', 0)
->get();
I have a redirect function that looks like this. For some reason, it looks like it's sometimes missing to increment the item. I'm wondering if it sometimes can be so that it misses this because some caching or something?
public function redirect($key)
{
$item = $this->items->findOrFail($key);
$item->increment('redirects');
$encode = urlencode($item->_url);
return view('item.redirect', ['url' => ($encode)]);
}
I'm not sure about topic of your question. Do you get such method somewhere from Laravel?
I don't know what is $key here but you should make sure this is single id (for example 2) and not array.
For single model:
$item->increment('redirects');
will work
but if $key sometimes is array then
$item = $this->item->findOrFail($key);
will be collection, so increment method would cause error, because there is no increment method on Eloquent collection.
I have the following code in my controller:
for($i=0; $i<$number_of_tourists; $i++) {
$tourist = Tourist::updateOrCreate([
'doc_number' => $request['doc_number'][$I]
],
$tourist_to_update);
}
Each time updateOrCreate runs, it does 1 of 3 things:
Updates the model instance; OR
Creates and saves a new one; OR
Leaves everything unchanged (if model with such values already exists)
I need to check if updateOrCreate has done the first one (updated) and then execute some code.
How can I do it?
You can figure it out like this:
$tourist = Tourist::updateOrCreate([...]);
if(!$tourist->wasRecentlyCreated && $tourist->wasChanged()){
// updateOrCreate performed an update
}
if(!$tourist->wasRecentlyCreated && !$tourist->wasChanged()){
// updateOrCreate performed nothing, row did not change
}
if($tourist->wasRecentlyCreated){
// updateOrCreate performed create
}
Remarks
From Laravel 5.5 upwards you can check if updates have actually taken place with the wasChanged and isDirty method.
isDirty() is true if model attribute has been changed and not saved.
wasChanged() is true if model attribute has been changed and saved.
There is also a property (not method!) wasRecentlyCreated to check if user was created or not.
$user = factory(\App\User::class)->create();
$user->wasRecentlyCreated; // true
$user->wasChanged(); // false
$user->isDirty(); // false
$user = \App\User::find($user->id);
$user->wasRecentlyCreated; // false
$user->wasChanged(); // false
$user->isDirty(); // false
$user->firstname = 'Max';
$user->wasChanged(); // false
$user->isDirty(); // true
$user->save();
$user->wasChanged(); // true
$user->isDirty(); // false
//You can also check if a specific attribute was changed:
$user->wasChanged('firstname');
$user->isDirty('firstname');
You can checkout the link to the laravel's documentation for wasChanged and isDirty methods.
https://laravel.com/docs/8.x/eloquent#examining-attribute-changes or
https://laravel.com/docs/9.x/eloquent#examining-attribute-changes
It is pretty easy to determine if the function resulted in an update or an insert (check the wasRecentlyCreated property). However, when using that function, it is less easy to determine if the update actually happened (if the model exists but is not dirty, no update will be performed). I would suggest not using that function, and splitting out the functionality yourself.
This is the function definition:
public function updateOrCreate(array $attributes, array $values = [])
{
$instance = $this->firstOrNew($attributes);
$instance->fill($values)->save();
return $instance;
}
To integrate this into your code, I'd suggest something like:
for ($i=0; $i<$number_of_tourists; $i++) {
$tourist = Tourist::firstOrNew(['doc_number' => $request['doc_number'][$i]]);
$tourist->fill($tourist_to_update);
// if the record exists and the fill changed data, update will be performed
$updated = $tourist->exists && $tourist->isDirty();
// save the tourist (insert or update)
$tourist->save();
if ($updated) {
// extra code
}
}
Okay so I couldn't find a good answer for my scenario.
I was using: $this->created_at == $this->updated_at however I would sometimes update the record later in the request, which meant that 20% of the time the created_at and updated_at were about 1ms out.
To combat this I created something a little more relaxed which allows an extra second between creation and modification.
public function getRecentlyCreatedAttribute()
{
return $this->wasRecentlyCreated || $this->created_at == $this->updated_at || $this->created_at->diffInSeconds($this->updated_at) <= 1;
}
I can now call $this->recentlyCreated which will return true if there is a small difference in time (1 second).
Tbh this is the second time I've needed this in a project, I'm posting as I just ended up googling it and coming back to this thread looking for the same answer.
If someone has a more elegant solution, hmu.
#patricus below presented a working way to solve the problem.
though #TheFallen here gave a solution which uses Eloquent Events and seems more elegant:
Laravel Eloquent Events - implement to save model if Updated
The model attribute 'wasRecentlyCreated' would only be 'true' if it has just been created.
There is property named 'changes' in model (it is an array), that determines whether the model has been updated with new values or it has been saved as is without making any changes to its attribute.
Check the following code snippet:
// Case 1 : Model Created
if ($model->wasRecentlyCreated) {
} else { // Case 2 : Model Updated
if (count($model->changes)) { // model has been assigned new values to one of its attributes and saved successfully
} else { // model has NOT been assigned new values to one of its attributes and saved as is
}
}
I've created an API using Laravel and I'm trying to find out how to cache Eloquent models. Lets take this example as one of the API endpoints /posts to get all the posts. Also within the method there are various filter options such as category and search and also gives the option to expand the user.
public function index()
{
$posts = Post::active()->ordered();
if (Input::get('category')) $posts = $posts->category(Input::get('category'));
if (Input::get('search')) $posts = $posts->search(Input::get('search'));
if ($this->isExpand('user')) $posts = $posts->with('user');
$posts = $posts->paginate($this->limit);
return $this->respondWithCollection($this->postTransformer->transformCollection($posts->all()), $posts);
}
I have been reading up and found in Laravel 4 you could cache a model like this
return Post::remember($minutes);
But I see this has been removed for Laravel 5.1 and now you have to cache using the Cache facade, but is only retrievable by a single key string.
$posts = Cache::remember('posts', $minutes, function()
{
return Post::paginate($this->limit);
});
As you can see, my controller method contains different options, so for the cache to be effective I would have to create a unique key for each option like posts_cagetory_5, posts_search_search_term, posts_category_5_search_search_term_page_5 and this will clearly get ridiculous.
So either I'm not coming across the right way to do this or the Laravel cache appears to have gone backwards. What's the best solution for caching this API call?
As the search is arbitrary, using a key based on the search options appears to be the only option here. I certainly don't see it as "ridiculous" to add a cache to for expensive DB search queries. I may be wrong as I came by this post looking for a solution to your exact problem. My code:
$itemId = 1;
$platform = Input::get('platform'); // (android|ios|web)
$cacheKey = 'item:' . $itemId . ':' . $platform;
$item = Item::find(1);
if( Cache::has($cacheKey) ) {
$result = Cache::get($cacheKey);
} else {
$result = $this->response->collection( $item, new ItemTransformer( $platform ) );
Cache::tags('items')->put($cacheKey, $result, 60); // Or whatever time or caching and tagged to be able to clear the lot in one go...
}
return $result;
I realise that my example has less complexity but it seems to cover all the bases for me. I then use an observer to clear the cache on update.
I'm working with the latest codeIgniter released, and i'm also working with jquery datatables from datatables.net
I've written this function: https://gist.github.com/4478424 which, as is works fine. Except when I filter by using the text box typing something in. The filter itself happens, but my count is completely off.
I tried to add in $res = $this->db->count_all_results() before my get, and it stops the get from working at all. What I need to accomplish, if ($data['sSearch'] != '') then to utilize the entire query without the limit to see how many total rows with the search filter exists.
If you need to see any other code other than whats in my gist, just ask and I will go ahead and post it.
$this->db->count_all_results() replaces $this->db->get() in a database call.
I.E. you can call either count_all_results() or get(), but not both.
You need to do two seperate active record calls. One to assign the results #, and one to get the actual results.
Something like this for the count:
$this->db->select('id');
$this->db->from('table');
$this->db->where($your_conditions);
$num_results = $this->db->count_all_results();
And for the actual query (which you should already have):
$this->db->select($your_columns);
$this->db->from('table');
$this->db->where($your_conditions);
$this->db->limit($limit);
$query = $this->db->get();
Have you read up on https://www.codeigniter.com/userguide2/database/active_record.html#caching ?
I see you are trying to do some pagination where you need the "real" total results and at the same time limiting.
This is my practice in most of my codes I do in CI.
$this->db->start_cache();
// All your conditions without limit
$this->db->from();
$this->db->where(); // and etc...
$this->db->stop_cache();
$total_rows = $this->db->count_all_results(); // This will get the real total rows
// Limit the rows now so to return per page result
$this->db->limit($per_page, $offset);
$result = $this->db->get();
return array(
'total_rows' => $total_rows,
'result' => $result,
); // Return this back to the controller.
I typed the codes above without testing but it should work something like this. I do this in all of my projects.
You dont actually have to have the from either, you can include the table name in the count_all_results like so.
$this->db->count_all_results('table_name');
Count first with no_reset_flag.
$this->db->count_all_results('', FALSE);
$rows = $this->db->get()->result_array();
system/database/DB_query_builder.php
public function count_all_results($table = '', $reset = TRUE) { ... }
The
$this->db->count_all_results();
actually replaces the:
$this->db->get();
So you can't actually have both.
If you want to do have both get and to calculate the num rows at the same query you can easily do this:
$this->db->from(....);
$this->db->where(....);
$db_results = $this->get();
$results = $db_results->result();
$num_rows = $db_results->num_rows();
Try this
/**
* #param $column_name : Use In Choosing Column name
* #param $where : Use In Condition Statement
* #param $table_name : Name of Database Table
* Description : Count all results
*/
function count_all_results($column_name = array(),$where=array(), $table_name = array())
{
$this->db->select($column_name);
// If Where is not NULL
if(!empty($where) && count($where) > 0 )
{
$this->db->where($where);
}
// Return Count Column
return $this->db->count_all_results($table_name[0]);//table_name array sub 0
}
Then Simple Call the Method
Like this
$this->my_model->count_all_results(['column_name'],['where'],['table name']);
If your queries contain a group by, using count_all_results fails. I wrote a simple method to work around this. The key to preventing writing your queries twice is to put them all inside a private method that can be called twice. Here is some sample code:
class Report extends CI_Model {
...
public function get($page=0){
$this->_complex_query();
$this->db->limit($this->results_per_page, $page*$this->results_per_page);
$sales = $this->db->get()->result(); //no table needed in get()
$this->_complex_query();
$num_results = $this->_count_results();
$num_pages = ceil($num_results/$this->results_per_page);
//return data to your controller
}
private function _complex_query(){
$this->db->where('a', $value);
$this->db->join('(subquery) as s', 's.id = table.s_id');
$this->db->group_by('table.column_a');
$this->db->from('table'); //crucial - we specify all tables here
}
private function _count_results(){
$query = $this->db->get_compiled_select();
$count_query = "SELECT count(*) as num_rows FROM (".$query.") count_wrap";
$r = $this->db->query($count_query)->row();
return $r->num_rows;
}
}