My website has Object as the model and ObjectSearch as the searchModel. I realized that I am already having a lot of duplicates in my two different controllers. For example, FirstController has this:
$arr = Yii::$app->db->createCommand('SELECT id, name, address
FROM hosts)->queryAll();
Then at some part of my website SecondController also needs to use that same query.
How can I re-use that query over and over again from different controllers?
I am talking about this:
public function search($params)
{
$query = Host::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]
]);
.
.
.
return $dataProvider;
}
I have another question also. I am planning to do different queries. What is the best approach for making another query? Should I use that
public function search($params)
again and maybe add on the parameter like this?
public function search($params, $cond)
{
if ($cond == true) {
$query = *something;
}
else {
$query = *something_else;
}
.
.
.
return $dataProvider;
}
In your root folder, you are having component folder. Inside component folder, create one page like UserInfoComponent.php. This page will be common and can be use anywhere.
This is the Directory Structure
YourProjectFolder
->assets
->commands
->components
->UserInfoComponent.php
.
.
UserInfoComponent.php
<?php
namespace app\components;
use Yii;
use yii\base\Component;
class UserInfoComponent extends Component
{
.
.
public function getUserDetails() {
$arr = Yii::$app->db->createCommand('SELECT id, name, address FROM hosts')->queryAll();
return $arr;
}
.
.
.
}
Now, how to call this function in your controller or view.
Controller or view
<?
$arrValue = Yii::$app->userinfo->getUserDetails();
?>
For common function you can create an helper class
namespace myapp\myhelperdir
class MyQueryHelper
{
public static function mySelectFunction ($param)
{
// your code for function
return $yourResult;
}
then you can refer to these function in every part of your project simply adding
use myapp\myhelperdir\MyQueryHelper
$myResult = MyQueryHelper::mySelectFunction($myParam);
Related
I am just doing my first exercises with Illuminate (Laravel) on my currently handmade database and website. I would like to improve by using MVC and found Illuminate interesting to use for the interaction with my database.
I worked the day on this bloody tiny code and can't find the problem and I hope someone out there has a good idea for me. Many thanks!
Basic question: Why can't I iterate over the courses to the given semester? While it is possible to call a specific course.
use Illuminate\Database\Eloquent\Model as Eloquent;
class Semester extends Eloquent {
protected $table = "tblSemester";
protected $primaryKey = "SemesterID";
public function getCourses() {
// test if semester is correct
echo $this->SemesterID, " - ", $this->Semestertext, "<br>";
// This works fine and returns the expected result
$course = $this->hasMany('Course', 'Semester')->first();
echo $course->Number, " - ", $course->Title;
// This doesn't work. It returns nothing.
// There seems to be no data in $courses (just metadata)
$courses = $this->hasMany('Course', 'Semester');
foreach ($courses as $course) {
echo $course->Number, " - ", $course->Title;
echo "<br>";
}
return "<h1>" . "Test ends" . "</h1>";
}
}
Many thanks!
Tim
First of all
Since you said you are new to 'Laravel' and Want to follow MVC, I thought I may give you some advices.
In laravel Models, don't include database manipulation codes. Instead write them in controllers.
In Models, only include functions which belongs to a single models instance (non static functions).
relationships
query scopes
accessors / modifiers
Example Model
class Semester extends Model
{
protected $table = 'semesters';
// relationships
public function cources()
{
return $this->hasMany(Cource::class);
}
}
Example Controller
class SemesterController extends Controller
{
public function iterateOverCourcesOfGivenSemester()
{
// take first semester as an example.
$givenSemester = Semester::first();
// all the cources belongs to a given semester.
$cources = $givenSemester->cources;
foreach($cources as $cource) {
// Warning! don't echo anything in a controller, Instead return them to a view.
echo $course->Number, " - ", $course->Title;
echo "<br>";
}
}
}
Why is this not working:
public function cources()
{
return $this->hasMany(Cource::class);
}
But this does:
public function cources()
{
return $this->hasMany('Course', 'Semester')->get();
}
class MyController extends Controller {
private $data;
index() {
$data = (object)[];
$this->data = $data;
return compact();
}
otherRouteMethod() {
dd($this->data);
}
}
ROUTES
Route::get('/my', MyController::class . '#index')->name('my.overview');
Route::get('/my/sub_route', MyController::class . '#otherRouteMethod');
Why is the following happening?
Both routes work
dd($this->data) prints "null" as it was not set in index()
using public modifier for $data leads to the same issue
Is it because Laravel makes a new instance of the class on every route change?
I'm currently rebuilding my vanilla-PHP-App with Laravel and I have the following problem.
I have multiple database-tables, that represent word categories (noun, verb, adverb, ...). For each table I created a separate Model, a route::resource and a separate resource-Controller. For example:
NomenController.php
public function show($id)
{
$vocab = Nomen::find($id);
return view('glossarium.vocab_update', compact('vocab'));
}
and
VerbController.php
public function show($id)
{
$vocab = Verb::find($id);
return view('glossarium.vocab_update', compact('vocab'));
}
...which are essentially the same except the Model class.
I don't want to create a separate Controller for each model, that does exactly the same. What would be the most simple and elegant way to solve this?
Should I just create a VocabController.php and add a parameter for the Model-name like:
Route::resource('/vocab/{category}', 'VocabController');
and then add a constructor method in this controller like
public function __construct ($category) {
if ($category == 'nomen') {
$this->vocab = App\Nomen;
}
else if ($category == 'verb') {
$this->vocab = App\Verb;
}
}
I wonder if there is a simpler method to do that. Can I somehow do this with Route Model Binding?
Thanks in advance
Simply create a trait like this in App\Traits, (you can name it anything... Don't go with mine though... I feel its pretty lame... :P)
namespace App\Traits;
trait CommonControllerFunctions {
public function show($id) {
$modelObject = $this->model;
$model = $modelObject::find($id);
return view('glossarium.vocab_update', compact('model'));
}
}
and in your NomenController and VerbController, do this:
use App\Traits\CommonControllerFunctions;
class NomenController {
use CommonControllerFunctions;
protected $model = Nomen::class;
}
and
use App\Traits\CommonControllerFunctions;
class VerbController {
use CommonControllerFunctions;
protected $model = Verb::class;
}
Note: Please note that this example is just a work-around for your particular situation only... Everyone practices code differently, so this method might not be approved by all...
I think the simpliest way it to create only one controller, eg VocabController with methods nomen, verb and whatever you want.
Routes:
Route::get('/vocab/nomen/{nomen}', 'VocabController#item');
Route::get('/vocab/verb/{verb}', 'VocabController#item');
And the model binding:
Route::model('nomen', 'App\Nomen');
Route::model('verb', 'App\Varb');
Then your method shoud look like that:
public function item($item)
{
return view('glossarium.vocab_update', $item);
}
Keep in mind, that $item is already fetched model from the database.
Thanks for watching my first question.
I have something confused.
How could I write the operations of database into database and don't write the function in every Controller?
I have considered middleware and find that must change my route register style.
my Route is this:
Route:resource('province','\\Modules\\Info\\Controllers\\P_ProvinceController');
Dose it has some awesome methods replace this?
public function Store(Request $request)
{
$params = $request->input('data');
$params['CreateID'] = Auth::user()->id;
$params['CreateName'] = Auth::user()->name;
$params['CreateTime'] = Carbon::now();
$province = P_ProvinceModel::Create($params);
$params['Pro_Is_Del'] = 1;
$log_info['table'] = $province->getTable();
$log_info['type'] = "create";
$log_info['user'] = Auth::user()->name;
$log_info['datetime'] = Carbon::now();
LogModel::create($log_info);
if($province){
return response()->json(array(
'status' => 200,
'msg' => '新增成功',
'data' => $province
));
}else
return response()->json(array(
'status' => 500,
'msg' => '保存失败',
));
}
Thanks.
Here is how I solved across model functionality
First Create a Trait that does what you want on save.
<?php
namespace App\Models\Observers;
trait CreatedByObserver
{
public static function bootCreatedByObserver(){
/** Simply means that whenever this model is creating a model do: */
static::creating(function($model){
if(auth()->check()){
$responsiblePerson = auth()->user()->first_name . " " . auth()->user()->last_name;
} else {
$responsiblePerson = "system";
}
/** You can set any model variables within */
$model->created_by = $responsiblePerson;
});
}
}
In there do all you need to do when a record is saved/created/updated/deleted
Then In all Models you want this behaviour used add the trait.
Check them out here : https://laravel.com/docs/5.2/eloquent#events
As far i understand your question, you are asking for way to make your controller an abstract type, i.e. controller just need to handle the route and view not any other things(like database,application logic etc) which is the philosophy of the laravel Framework.
To make your controller abstract (meaning of abstract as explained aboave),
first you need to understand, "What your application logic are and what your database logic are ?"
when you understand these two things then, you can easily separate your aapplication logic and databasse logic from your controller.
For example :
For keeping your Application logic you can make service folder in your root of your project also you can make folder name 'Dao' (Database access object) in the same path of service . You need to keep these folder in autoload from your composer. Just make class for service and your Dao.
And now your application follow will be,
First Route, will hit controller then controller will need to call some method in service and then service will call the respective DAO . method.
Example :
Controller/YourController.php
Class YourController extends Controller {
public function Store(Request $request,yourservice,$yourService)
{
$this->myservice = $yourservice;
$this->myservice->store('your inputs request');
return $something ;
}
}
service/yourService.php
Class yourService {
public function store($yourinputs,yourDao $mydao){
$this->mydao = $mydao;
//you can use your application logic here
return $this->mydao->create($yourinputs);
}
And now the turn is for DAO :
dao/yourdao.php
use model // use your model here .
class yourDao {
public function create($yourdata,yourmodel $model){
$this->model = $model;
return $this->model->create($yourdata);
}
}
Now, you can see the controller just save the data in database, but don't know how it is saving the data and what are the application logic.
This explanation is just a simple way of doing project to make a controller abstract. There are other various ways of doing this. For example you can see Repository Design Pattern , which also used by laravel core .
Hope this explanation will not bore anyone . :) Happy coding .
I am working in a app where I need to create database tables on the fly, I did that. But now I need an elegant way to access the database tables. So here is my problem
In regular practice we create a database table and create a class to map a table (ORM). But I cannot take that approach in this case. THis is what I have done so far..
I am extending the MY_Model in the Crud Model, I try to setup the database table name in the set_table() method. It works fine.
class Crud extends MY_Model
{
private $customer_id;
public function __construct()
{
parent::__construct();
$this->_database = $this->load->database('customer', TRUE);
}
public function set_table($customer_id, $table_name)
{
$this->customer_id = $customer_id;
$this->_table = 'tbl_' . $customer_id . '_' . $table_name;
}
}
Test.php Controller to test the code.
class Test extends CI_Controller
{
public function index(){
$this->load->model('editor/crud');
var_dump($this->crud->set_table(1, 'alpha'));
var_dump($this->crud->get_all());
}
}
But I need an elegant way to access the table. eg.
$this->alpha->get_all()
OR
$this->crud->alpha->get_all()
would be easy to understand & it will make much sense. Is it actually possible to do in PHP? Any help would be much appreciable.
I'm guessing you can do something like:
class Test extends CI_Controller
{
public function index(){
$this->load->model('editor/crud');
$this->crud->set_table(1, 'alpha');
$this->alpha = clone $this->crud; // in case you use set_table again
$info = this->alpha->get_all();
}
}
If I recall correctly, you should be able to do it in the model too.
public function set_table($customer_id, $table_name)
{
$this->customer_id = $customer_id;
$this->_table = 'tbl_' . $customer_id . '_' . $table_name;
$this->{$table_name} = clone $this;
}
and then access it like: $this->crud->alpha->get_all();