Laravel load settings from cache - php

I'm using Laravel 5.7, and I'm saving settings in the database.
public function store(Request $request)
{
$data = $request->only('app_name', 'app_desc', 'company_phone', 'company_address',
'service_wage', 'kavenegar_api', 'kavenegar_number'
);
foreach ($data as $key => $value) {
Setting::updateOrInsert(
['name' => $key],
['val' => $value]
);
}
return redirect()->back()->withFlashSuccess("saved");
}
I can get settings with the following helper.
if (!function_exists('getSetting')) {
function getSetting($key)
{
return Setting::where('name', $key)->value('val');
}
}
But there is a problem; We are listing all settings and calling getSetting('setting_name') multiple times which is making one query to the database for each call. That’s a lot of queries to get the settings.
I want to use the cache but how can I when the settings are stored in the database? I also save them in the cache.

It's pretty straightforward. Consult the docs on caching for details, but the basic idea is this:
function getSetting($key)
{
return Cache::remember('setting:' . $key, 3600, function() use($key) {
return Setting::where('name',$key)->value('val');
});
}
You could also cache all the settings into a single cache value. Depending on how many of the settings there are and what proportion you use in an average request, that may or may not be a good approach.

try this
if (!function_exists('getSetting'))
{
function getSetting($key)
{
return Setting::where('name', $key)->first()->val;
}
}
And Dont forget to add the Namespace App\Setting
How does the function Work
For Example if you have the setting named as app_name and While You pass the value to the function
Setting::where('name', $key)->first()->val;
getSetting('app_name'); it will find the first record with the key app_name and select the val field value from the object and returns it
UPDATED FOR THE CACHE
THIS IS UNTESTED FUNCTION
function getSetting($name)
{
if (Cache::has('setting_'.$name)) {
return Cache::get('setting_'.$name);
}
$query = Setting::where('name', $key)->first();
Cache::forever('setting_'.$name, $query->val);
return $query->val;
}
This Will remember the cache forever and visit the documentation to read about the cache
https://laravel.com/docs/5.8/cache
And dont forget to add the use Illuminate\Support\Facades\Cache; in the namespace list

Related

How to make autonumber restart every year in Laravel/PHP?

I made code like this in Models Number_npe:
public function nomor_akhir()
{
$query = DB::table('nomor_npe')
->select('*')
->orderBy('id','DESC')
->first();
return $query;
}
Then the Controllers:
public function nomor_npe_store(Request $req)
{
$tanggal_npe = $req->input('tanggal_npe');
$pesan = new Nomor_npe();
$check = $pesan->nomor_akhir();
if($check) {
$nomor_npe = $check->nomor_npe+1;
}else{
$nomor_npe = 1;
}
DB::table('nomor_npe')->insert([
'nomor_npe' => $nomor_npe,
'tanggal_npe' => $tanggal_npe
]);
return redirect('nomor_npe')->with('success','Nomor NPE berhasil ditambahkan');
}
The Add NPE Number display looks like this:
When I click Save, the number_npe has been successfully added automatically.
But I want to make when the year changes, the number_npe restarts automatically from 1 again ... Please help everyone who knows
I have to write this as an answer, but it is not 100% an answer to your code, these are just tips for you to have better code. (So if anyone sees this too, they are aware too)
First of all, avoid 100% writing code in other language than English, as we are following it (we do not speak your language) and we do not understand nearly anything unless we use a Translator...
So, if you are going to use Laravel, try to avoid using DB, when you can just use the Model (hopefully you have created it...).
So your class should look like this:
public function lastNumber()
{
return NomorNpe::orderByDesc('id')->first();
}
Then your controller should be like:
public function store(Request $request, NomorNpe $nomor_npe)
{
NomorNpe::create([
'nomor_npe' => $nomor_npe->lastNumber() ? $nomor_npe->lastNumber()->nomor_npe + 1 : 1,
'tanggal_npe' => $request->input('tanggal_npe')
]);
return redirect('nomor_npe')->with('success', 'Nomor NPE berhasil ditambahkan');
}
See how I reduced everything from 13 lines of code to 5 lines of code and is 100% readable... (Or 9 lines to 2)
Make sure to use what Laravel brings you as "default" for it, use Models not DB::table('xxx'), take advantage of Eloquent.
Use this code for starting the number from 1, when the year changed:
public function nomor_npe_store(Request $req) {
$tanggal_npe = $req->input('tanggal_npe');
//---Current Date
$date = date('Y-m-d', time());
//---NOMOR NPE
$nomor_npe = DB('number_npe')->whereYear('tanggal_npe', $date)->max('normor_npe');
if (!$nomor_npe) {
$nomor_npe = 1;
} else {
$nomor_npe++;
}
DB::table('nomor_npe')->insert([
'nomor_npe' => $nomor_npe,
'tanggal_npe' => $tanggal_npe
]);
return redirect('nomor_npe')->with('success','Nomor NPE berhasil ditambahkan');
}

How to add values to cache

I am using file cache in yii2 framework.
My question is
Is it possible to add some extra value to cache without refresh the cacheFile.Suppose i create cache file for my products now on each entry i update cache file. I want to add just the new product to cache.
How can i do that thanks in advance
This is my Code
public static function updateCache(){
$product_grid = Yii::$app->db->createCommand("CALL get_products()")->queryAll();
Yii::$app->cache->set('product_grid', $product_grid);
}
I write store procedure for getting all products,now when i add new product each time i am calling the updateCache function which regenerate the products and add it to cache due to which application speed may be effected.
This is the code for addingProduct and updateCache:
public function actionCreate($id = NULL) {
$model = new PrProducts();
if ($model->load(Yii::$app->request->post())) {
$model->save(false);
self::updateCache();
}
}
Native Yii2 cache components doesn't allow to update existing cache items partially.
But you can do this manually:
public static function addToCache($modelProduct) {
$productGrid = Yii::$app->cache->get('productGrid');
$productGrid[$modelProduct->id] = $modelProduct->attributes;
Yii::$app->cache->set('productGrid', $productGrid);
}
But I recommend other way: you can store each product record as separate cache item.
Firstly you can add multiple items:
public static function refreshProductCache() {
// Retrieve the all products
$products = Yii::$app->db->createCommand("CALL get_products()")->queryAll();
// Prepare for storing to cache
$productsToCache = [];
foreach ($products as $product) {
$productId = $product['id'];
$productsToCache['product_' . $productId] = $product;
}
// Store to cache (existing values will be replaced)
Yii::$app->cache->multiSet($productsToCache);
}
Secondly you can update cache when you read data. For instance:
public function actionView($id) {
$model = Yii::$app->cache->getOrSet('product_'.$id, function() use ($id) {
return PrProducts::find()
->andWhere(['id' => $id])
->one();
});
return $this->render('view', ['model' => $model]);
}
This code creates cache only one time for each $id that not yet present in the cache.
Thirdly you can add individual products to cache right after create/update. For instance:
public static function addToCache(PrProducts $modelProduct) {
$productId = $modelProduct->id;
Yii::$app->cache->set('product_' . $productId, $modelProduct);
}
I think this approach more flexible. Of course, it may be less efficient than you way. It very depends from code that reads your cache.

How to retrieve data settings from database array to have globally acces in Laravel 5

How to retrieve global settings from database table ?
I know how to create global settings in array like this:
path:
app/config/settings.php
<?php
return array(
'admin_email' =>'mail#shabeebk.com',
'admin_name' =>'Admin',
);
and in controller:
$cvalue = Config::get('settings.admin_name');
I'd like to achive the same effect but this time data receive from table settings
$data = Settings::get();
return as array and have globally acces to them.
There are multiple ways you can accomplish this, here are two options that come to mind.
Option 1: preload
In a ServiceProvider boot() method (maybe your AppServiceProvider) you could easily load up all DB settings into config:
Settings::get()->each(function($setting) {
// Assumes the columns in your DB are 'key' and 'value'
Config::set('settings.' . $setting->key, $setting->value);
});
Now you can just grab Config::get('settings.foo') globally.
Option 2: query as-needed
Or you could write a get() method on your Settings model that provides similar behavior:
public static function get($key, $default = null)
{
if($match = self::where('key', $key)->first()) {
return $match->value;
}
return $default;
}
Now you can use Settings::get('foo') globally.

Laravel detect if there is a new item in an array

I want to implement a system in my project that "alerts" users when there is a new comment on one of their posts.
I currently query all comments on the posts from the logged in user and put everything in an array and send it to my view.
Now my goal is to make an alert icon or something when there is a new item in this array. It doesn't have to be live with ajax just on page load is already good :)
So I've made a function in my UsersController where I get the comments here's my code
public function getProfileNotifications()
{
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
//comments
if (!empty($projects)) {
foreach ($projects as $project) {
$comments_collection[] = $project->comments;
}
}
if (!empty($comments_collection)) {
$comments = array_collapse($comments_collection);
foreach($comments as $com)
{
if ($com->from_user != Auth::user()->id) {
$ofdate = $com->created_at;
$commentdate = date("d M", strtotime($ofdate));
$comarr[] = array(
'date' => $ofdate,
$commentdate,User::find($com->from_user)->name,
User::find($com->from_user)->email,
Project::find($com->on_projects)->title,
$com->on_projects,
$com->body,
Project::find($com->on_projects)->file_name,
User::find($com->from_user)->file_name
);
}
}
} else {
$comarr = "";
}
}
Is there a way I can check on page load if there are new items in the array? Like keep a count and then do a new count and subtract the previous count from the new one?
Is this even a good way to apprach this?
Many thanks in advance! Any help is appreciated.
EDIT
so I added a field unread to my table and I try to count the number of unreads in my comments array like this:
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
//comments
if (!empty($projects)) {
foreach ($projects as $project) {
$comments_collection[] = $project->comments;
}
}
$unreads = $comments_collection->where('unread', 1);
dd($unreads->count());
But i get this error:
Call to a member function where() on array
Anyone any idea how I can fix this?
The "standard" way of doing this is to track whether the comment owner has "read" the comment. You can do that fairly easily by adding a "unread" (or something equivalent) flag.
When you build your models, you should define all their relationships so that stuff like this becomes relatively easy.
If you do not have relationships, you need to define something like the following:
In User
public function projects()
{
return $this->hasMany('App\Models\Project');
}
In Project
public function comments()
{
return $this->hasMany('App\Models\Comment');
}
Once you hav ethose relationshipt, you can do the following. Add filtering as you see fit.
$count = $user->projects()
->comments()
->where('unread', true)
->count();
This is then the number you display to the user. When they perform an action you think means they've acknowledged the comment, you dispatch an asynchronous request to mark the comment as read. A REST-ish way to do this might look something like the following:
Javascript, using JQuery:
jQuery.ajax( '/users/{userId}/projects/{projectId}/comments/{commentId}', {
method: 'patch'
dataType: 'json',
data: {
'unread': false
}
})
PHP, in patch method:
$comment = Comment::find($commentId);
$comment->update($patchData);
Keep in mind you can use Laravel's RESTful Resource Controllers to provide this behavior.
try this
$unreads = $project->comments()->where('unread', 1);
dd($unreads->count());
EDIT
My be Has Many Through relation will fit your needs
User.php
public function comments()
{
return $this->hasManyTrough('App\Project', 'App\Comment');
}
Project.php
public function comments()
{
return $this->hasMany('App\Comment');
}
then you can access comments from user directly
$user->comments()->where('unread', 1)->count();
or I recommend you define hasUnreadComments method in User
public function hasUnreadComments()
{
$return (bool) $this->comments()->where('unread', 1)->count();
}
P.S.
$uid = Auth::user()->id;
$projects = User::find($uid)->projects;
this code is horrible, this way much better
$projects = Auth::user()->projects;

Protect routes dynamically, based on id (laravel, pivot table)

This topic has been discussed a lot here, but I don't get it.
I would like to protect my routes with pivot tables (user_customer_relation, user_object_relation (...)) but I don't understand, how to apply the filter correctly.
Route::get('customer/{id}', 'CustomerController#getCustomer')->before('customer')
now I can add some values to the before filter
->before('customer:2')
How can I do this dynamically?
In the filter, I can do something like:
if(!User::hasAccessToCustomer($id)) {
App::abort(403);
}
In the hasAccessToCustomer function:
public function hasCustomer($id) {
if(in_array($id, $this->customers->lists('id'))) {
return true;
}
return false;
}
How do I pass the customer id to the filter correctly?
You can't pass a route parameter to a filter. However you can access route parameters from pretty much everywhere in the app using Route::input():
$id = Route::input('id');
Optimizations
public function hasCustomer($id) {
if($this->customers()->find($id)){
return true;
}
return false;
}
Or actually even
public function hasCustomer($id) {
return !! $this->customers()->find($id)
}
(The double !! will cast the null / Customer result as a boolean)
Generic approach
Here's a possible, more generic approach to the problem: (It's not tested though)
Route::filter('id_in_related', function($route, $request, $relationName){
$user = Auth::user();
if(!$user->{$relationName}()->find($route->parameter('id')){
App::abort(403);
}
});
And here's how you would use it:
->before('id_in_related:customers')
->before('id_in_related:objects')
// and so on

Categories