Create value with prefix and auto increment in migration using laravel - sql

I want to create a database table in laravel using migration, I have 4 columns in that table
1) ID(Auto-Increment, Primary Key)
2) Book Name
2) Book ID
4) Price
Now, I need to automatically fill value of BOOK ID column, value like this
Book ID = 'Book_1' (here "Book_" is prefix & 1 is value from ID column)
so for auto increment we create like this
$table->increments('id');
I need for BookID, how to write for that.

possible solution.
NOTE : not in table creation (migration) but when actually storing data.
create an ordinary varchar column to store name.
$table->string('name');
in the AppServiceProvider class boot() function. do something like this.
let's imagine your particular model is 'Book'
Book::created(function ($book) {
$book->update(['name' => 'Book_' . $book->id]);
}
this will bind an event to the 'Book' creation. when every time new book is saved to the database, name will automatically generate and save.

If the BookID column is only for the representational purpose you can add an accessor.
public function getBookIdAttribute()
{
return 'Book_' . $this->attributes['id'];
}
else, you can accomplish by adding this to your model,
protected static function boot()
{
parent::boot();
static::created(function ($model) {
$model->BookID = 'Book_' . $model->id;
$model->save();
});
}

Related

Laravel ,php -artisan suffixe had to my table when i seed it

I have 2 small issues with the "php artisan db:seed" command.
WHen i run the command, i have this error message :
"SQLSTATE[42S02] Base table or view not found : 1146 La table
"bootstrap_template_commerciauxes" n'existe pas ..."
The problem is : my table name is commerciaux, and not commerciauxes.
I checked all my file, my model is Commerciaux.php, my factory CommerciauxFactory.
So ... what kind of sorcely is it ? I'am missing something ?
Secondly, the SQL request from db:seed add some columns i dont want to :
SQLSTATE[42S02]: Base table or view not found: 1146 La table 'bootstrap_template.commerciauxes' n'existe pas (SQL: insert into commerciauxes (nom, prenom, ville, updated_at, created_at) values (Dr. Luis Champlin PhD, Dr. Luella Leuschke, Leathaberg, 2022-06-03 21:42:44, 2022-06-03 21:42:44))
Here is my Commerciaux model :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Commerciaux extends Model
{
use HasFactory;
protected $fillable = [
'nom',
'prenom',
'ville',
'nbre_commande',
];
}
My CommerciauxFactory (in case)
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommerciauxFactory extends Factory
{
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'nom' => $this->faker->name(),
'prenom' => $this->faker->name(),
'ville' => $this->faker->city(),
];
}
}
Thanks you very much for your time, i wanted to try this nice tool but i get blocked since 2 days on thoses mistakes.
To answer your issues:
Laravel by default treats table names as plural due to default conversion and I would advise keeping it that way. If you want to define your own table name then, In your model Class you can define following for your name of table:
protected $table = 'commerciaux';
Also, in your migration's Up function, set your table name like following:
public function up()
{
Schema::create('commerciaux', function (Blueprint $table) {
//Your table columns and structure
});
}
Regarding the additional columns, those are laravel timestamps that keep a track of the timestamps of the record when it was created (created_at) and updated(updated_at) last time. In this case, I would also suggest keeping these fields as they keep a track of record creation and last modifying timestamps.
If you don't want these fields in your table then in your model you can define the following code to exclude the timestamps:
public $timestamps = false;
Other than that, you can also remove following line from your migration:
table->timestamps();
EDIT: Before running migration again, try the roll back command so the created base table and migration records can get deleted from the migrations table.

Laravel/SQL: How to fetch data from multiple table in a single query? that too using 'where'

Working on a search functionality on Laravel App(Blog/Posts).
There are multiple types of posts (each having a separate table in the database)
Like Business posts, Social Life posts etc..
Below is the search function on SearchController
class SearchController extends Controller
{
public function search(Request $request, $query = null)
{
if($query == null)
return redirect()->route('home');
$search = Business::where([['title','like','%'.$query.'%'],['status','=',1]])
->orWhere([['description','like','%'.$query.'%'],['status','=',1]])
->paginate(10);
return view('front.search',[
'results' => $search,
'query' => $query
]);
}
}
So basically my question is how to add other types of Post's table also?
My main motive is that when someone searches for anything, the result should be fetched from all types of posts table(business, nature, life & so on..).
You have to maintain common id in both the table
NOTE: Join is the preferable method
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);
If you have a model called Book, like this:
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
Then you can retrieve all of your books with authors like this:
$books = App\Book::with(['author'])->get();
Check out Eager loading from Laravel documentation.
Just add table name before every field
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

Laravel 5.5, Display news data from two tables

I am new to laravel and I experience some trouble. I try to obtain data stored in two different tables and display them:
News.php (model)
public static function Data($category) {
$perPage = config('var.news.perPage');
if ($category) {
$news = News::orderBy('id', 'desc')->where('category', $category)->SimplePaginate($perPage);
} else {
$news = News::orderBy('id', 'desc')->SimplePaginate($perPage);
}
return $news;
}
This is how I grab all data from News table which struct is:
id, title, body, created_at updated_at, created_by, updated_by, category
The category column contains values separated by comma, e.g. 1,2,3,4
Now, I have another table, News_Cat which has id, name columns.
In another method I try to grab the filters names against values stored in category column of News table
public static function getFilterNames($id) {
$filters = DB::table('News_Cat')
->select('News_Cat.name as name')
->leftJoin('News', DB::raw('CAST(News_Cat.id as nvarchar)'), DB::raw('ANY(SELECT(News.category))'))
->where('News.id', $id)
->get();
return $filters;
}
However, it completely does not work. What I try to achieve is to display filter name in view.blade as 'name' value for specified filter from News_Cat
#if($news->count())
#foreach($news as $article)
<a href="{{ route('news.show', $article->id) }}" class="item angled-bg" data-filters="{{ $filters }}">
<div class="row">
So as result I would get e.g. data-filters="news, update, hot, latest"> instead data-filters="1,2,3,4">
Thank you
You should use eloquent!
In your News Model
public function getFiltersAttribute(){
$categories = explode(',', $this->category);
return implode(', ', NewsCat::find($categories)->pluck('name')->toArray());
}
then in your view :
{{ $article->filters }}
will output news, update, hot, latest
BUT
You should use a pivot table between your categories and your news, it would be much easier.
This method can't allow you to eager load the relationship and make a request for each news
If you can't change your database structure, I can propose you this:
In the boot method of your AppServiceProvider:
Config::set('tags', NewsCat::all());
THEN
public function getFiltersAttribute(){
$categories = explode(',' $this->category);
return implode(', ', config('tags')->whereIn('id', $categories)->pluck('name')->toArray());
}
MANY TO MANY METHOD
I am using laravel naming convention for the table :
news, categories_news (the pivot), and categories
You will have 2 models : New and Category
In your New Model
public function categories(){
return $this->belongsToMany(Category::class)
}
in your Category Model :
public function news(){
return $this->belongsToMany(New::class);
}
if you are not using laravel naming conventions, you will have to customize these raltionship like this : https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

Transaction in cakephp- 3.0

In my cakephp3.0 application registration page the registration data is inserting into two tables. I am using query builder for doing this. After the successful insertion of data into the first table(Projects table) projectId(primary key of Projects table which is auto incremented value) will return and which is used to insert into second table that is ProjectCustomers table, in projectCustomers table projectId is used as forign key. My controller look like this
public function initialize() {
parent::initialize();
$this->Project = new ProjectsTable();
$this->ProjectCustomer = new ProjectCustomersTable();
}
public function add() {
if ($this->request->is('post')) {
$project_id = $this->Project->putProject($formData['survey_id'], $formData['title'], $formData['operator'], $this->Auth->user('id'));
$this->ProjectCustomer->putProjectCustomer($project_id, $formData['air_id'], $formData['email'], $formData['name'], $formData['company_name'], $formData['department_name'], SYS_ADMIN, PROJECT_CUSTOMER_STATUS_ACTIVE, $date, $date);
return $this->redirect(['action' => 'view', $project_id]);
}
}
What I need is that I want to make this two insertion action in a transaction so that if second insertion is failed anyhow, the first insertion in Projects table should roll back. Thank in advance for the help...

CRUD very slow with conditions, is there any other faster way?

im having problem with CRUd now that i filled the database. CRUD is taking ages to show, becouse it takes condition from M:M tables.
Tables:
Table USER. has many labels (hasMany)
Table LABLE, has many users (hasMany)
Intermidiate Table UserLabel, has two hasOne
I want to show all users from some label with CRUD like this:
MODEL USER:
class Model_User extends Model_Table {
public $table ='user';
function init(){
parent::init();
$this->addField('fbid')->mandatory('Facebook id required');
...
$this->hasOne('Application');
$this->hasMany('UserLabel');
$this->addExpression('ratio')->set(function($model,$select){
return $select->expr('ROUND(([f2] / [f1]) * 100,0)')
->setCustom('f1',$model->getElement('sends'))
->setCustom('f2',$model->getElement('clicked'));
});
$this->addHook('beforeSave',function($m){
$m['updated']=$m->dsql()->expr('now()');
});
}
MODEL LABEL:
class Model_Label extends Model_Table {
public $table ='label';
function init(){
parent::init();
$this->addField('name')->mandatory('Name required');
$this->addFIeld('application_id')->refModel('Model_Application')->defaultValue($this->api->recall('app'))->system(true);
$this->addField('active')->type('boolean')->defaultValue('true')->system(true);
$this->addField('created')->type('timestamp')->defaultValue($this->dsql()->expr('now()'))->system(true);
$this->addField('updated')->type('timestamp')->system(true);
$this->hasMany('UserLabel');
$m = $this->add("Model_UserLabel");
$this->addExpression("users", $m->dsql()
->field($m->dsql()->expr("count(*)"), "all users")
->where("label_id", $this->getField("id"))
);
MODEL USER LABEL
class Model_UserLabel extends Model_Table {
public $table ='userlabel';
function init(){
parent::init();
$this->hasOne('User');
$this->hasOne('Label');
}
}
CODE FOR CRUD
$c = $this->add('CRUD');
$c->setModel('User', array('name', 'gender','country','city'));
$c->model->addCondition('id','in',
$this->add('Model_UserLabel')->addCondition('label_id', $_GET['l'])->dsql()->field('user_id')
);
Is there any better way to do this?
ps. I tested this solution, it is a lot faster but still very slow at around > 5.000 users:
//get all users
$records = $this->api->db->dsql()->option('distinct')->table('user')->join('userlabel.user_id')->field('user.id')->where('userlabel.label_id',$_GET['l'])->do_getAll();
foreach($records as $record){
$users .= ','.$record['id'];
}
//create CRUD
$c = $this->add('CRUD');
$c->setModel('User', array('name', 'gender','country','city','sends','clicked','ratio'));
$c->model->addCondition("application_id", $this->api->recall('app'));
$c->model->addCondition('id','in',
'('.$users.')'
);
Source code express more than words, so you better add your model definition source code (maybe not full) in your question.
What should be one row in your CRUD/Grid? I guess it's not 1 user = 1 row, but 1 user_label should be one row in grid. So you should set UserLabel model as model for your grid.
And then define some additional fields in Model_UserLabel by joining them from user and/or label tables directly like this:
class Model_UserLabel extends SQL_Model {
function init() {
parent::init();
// ...
// fields from user table
$join_u = $this->join('user', 'user_id');
$join_u->addField('username'); // this adds fields in current model from joined table
$join_u->addField('email');
// fields from label table
$join_l = $this->join('label', 'label_id');
$join_l->addField('name');
}
}
Note: source code above is untested and put here only as example.
EDIT:
Try this solution - almost the same as I wrote earlier above:
MODEL USER LABEL
class Model_UserLabel extends Model_Table {
public $table ='userlabel';
function init(){
parent::init();
$this->hasOne('User');
$this->hasOne('Label');
// join user table and add fields to this model from joined user table
$j = $this->join('user', 'user_id');
$j->addField('name');
$j->addField('gender');
$j->addField('country');
$j->addField('city');
}
}
CODE FOR CRUD
$m = $this->add('Model_UserLabel'); // UserLabel here not User
$m->addCondition('label_id', $_GET['l']); // and then this is simple
$c = $this->add('CRUD');
$c->setModel($m, array('name', 'gender','country','city'));
Try this solution and as (almost) always - code is untested.
EDIT:
Please try this version - is it working faster? That's basically your P.S. example, but you shouldn't extract all user IDs, join them and then create huge select with a lot of 'in'.
Faster result should be if you could do all with just one DB request without any additional processing of data.
// parameters
$app_id = $this->api->recall('app);
$label_id = $_GET['l'];
// prepare model for grid
$m = $this->add('Model_User'); // default User model
$m->_dsql()->option('distinct') // add join to userlabel table + conditions
->join('userlabel.user_id')
->where('userlabel.label_id', $label_id)
->where($m->getField('application_id'), $app_id);
// create CRUD and set it's model. All conditions already set on model above
$c = $this->add('CRUD');
$c->setModel($m, array('name', 'gender','country','city','sends','clicked','ratio'));
NOTE: Source code above as often - untested :)