Laravel - multiple forms/tables - carry over variable in URL to fill table column - laravel-5.7

I am quite new to coding and to Laravel (using 5.7). I understood how to create a basic one-page form to populate a table in a database, including relashionship and authentication but I have difficulties reaching the next level.
The app I want to design will have multiple forms and tables and I can't figure out how to link info collected from the first form to the following one.
Let's consider a Database with:
- a Clients table (populated thru form page A) ex: one field is a client_id field.
- a Products table (populated thru form page B)
- (ultimately they will be more)
When the user (e.g. an employee of a compagny analyzing clients behavior) is done filling the form page A (URL: /clients, GET method: Clientcontroller#create, view clients.create.blade.php), he/she click Next.
My idea is that the Next button should:
- submit information to the Client table (POST method: Clientscontroller#store)
- and redirect the user to the page B of the form, carrying over the client_id in the URL (URL: /products/{client_id}/create, GET method: Productscontroller#create, view products.create.blade.php).
In the Product table, I have a client_id column and I have a one to many relashionship between the Clients and Products model.
Upon submission of form B, I would like to retrieve the {client_id} from URL to fill the client_id column of the Product table but I am stuck here. I would appreciate pieces of guidance and advices. For simplification during the learning process, I consider that Clients only buy one product.
THE MAIN QUESTION IS:
- How to retrieve the {client_id} parameter from the URL of the products.create.blade.php view to inject it onto the client view (already tried a lot of thing from answer to similar questions in stackoverflow)
ALSO:
- Am I using the right approach? Any suggestions/advices?
EXTRA QUESTION a bit out of the scope:
- Any hint on how implement add/remove fields for products?
WEB ROUTES FILE:
> <?php
> Route::get('/', 'PagesController#welcome')->name('welcome');
> Auth::routes();
> //ROUTES FOR CLIENT
> Route::resource('clients','ClientsController');
> //ROUTES FOR PRODUCT (please note the {client_id} parameter)
> Route::get('/products/{client_id}/create', 'ProductsController#create')->name('products.create');
> Route::post('/products/{client_id}', 'ProductsController#store')->name('products.store');
> //not sure if it should be Route::post('/products', 'ProductsController#store')->name('products.store');
CLIENTS CONTROLLER:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Client; //THIS IS THE CLIENT MODEL
use App\User; ////THIS IS THE USER MODEL
class ClientsController extends Controller
{
AUTH
public function __construct()
{
$this->middleware('auth');
}
//INDEX (CLIENT)
public function index()
{
//my code here
}
//CREATE (CLIENT)
public function create()
{
$datas=[
'controllermethod' => 'ClientsController#store',
'client_id_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_id', //this what i am carrying over in URL
'input_label_l' => 'Client ID :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'client_info_1_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_info_1'
'input_label_l' => 'Client Info 1 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'client_info_2_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'client_info_2'
'input_label_l' => 'Client Info 2 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
]
],
return view ('clients.create')->with($datas);
}
//STORE (CLIENT)
public function store(Request $request)
{
$this->validate($request, [
'client_id' => 'required',
]);
$client = new Client;
$client->client_id = $request->input('client_id');
$client->client_info_1 = $request->input('client_info_1');
$client->client_info_2 = $request->input('client_info_2');
$client->user_id = auth()->user()->id; //one to many relashionship betw user/client models
$client->save();
//
//this is how I inject the {client_id} parameter onto the URL
//this works, the products.create view is displayed, the URL contain the client_id from formA entered by the user
$client_id = $request->client_id;
return redirect("/products/$client_id/create")->with('client_id', $client_id)
->with('success','New client record created');
//SHOW/DESTROY/EDIT/UPDATE FUNCTIONS....all this work
PRODUCTS CONTROLLER = THAT's WERE I AM STUCK + NOT SURE IF IT's THE RIGHT APPROACH
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
//note sure if I need that
//use Illuminate\Routing\Route;
use App\Product;
use App\Client;
use App\User;
class ProductsController extends Controller
{
//AUTH
public function __construct()
{
$this->middleware('auth');
}
//INDEX (PRODUCT)
public function index()
{
//no code so far
}
//INDEX (PRODUCT)
public function create()
{
$datas=[
'controllermethod' => 'ProductsController#store',
//planning of having dynamic add/remove products fields but let's keep it simple (1 client -> 1 product)
'product_id_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_id',
'input_label_l' => 'Date of cancer diagnosis :',
'input_default_l' => ''
],
'product_info_1_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_info_1'
'input_label_l' => 'Product Info 1 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
],
'product_info_2_field' => [
'input_type_l' => 'textinput',
'input_name_l' => 'product_info_2'
'input_label_l' => 'Product Info 2 :',
'input_placeholder_l' => 'XXXXX',
'input_default_l' => ''
]
],
//Below, I am not sure I should do that
return view ('products.create')->with($datas);
}
// STORE (PRODUCT) = THAT's WHERE I AM STUCK
// everything works except that the client_id column in the products table stays empty or filled with crap
public function store(Request $request)
{
//NOT SURE WHAT TO DO HERE TO RETRIEVE THE {client_id} from the "/products/$client_id/create" ...
//TO FURTHER INJECT IT AS A VALUE IN THE client_id COLUMN OF THE PRODUCT TABLE
//I KNOW IT's CRAP, but I TRIED THINGS ALONG THOSE LINES:
//return $client_id;
//$client_id = request()->route()->paremeters('client_id');
//$client_id = request()->route('client_id');
//$client_id = $request->client_id;
//$client_id = url('client_id');
$product = new Diagnosis;
$product->product_id = $request->input('product_id');
$product->product_info_1 = $request->input('product_info_1');
$product->product_info_2 = $request->input('product_info_2');
$product->user_id = auth()->user()->id;
//I KNOW IT's CRAP, but I TRIED THINGS ALONG THOSE LINES:
//$diagnosis->client_id = $client_id; //NB: if I write $diagnosis->client_id = 'whatever'; it works
$diagnosis->save();
//redirect to client.index view
return redirect('/clients')->with('success','New patient diagnosis (-es) created');
}

our solution will start by looking to this route :
Route::get('/products/{client_id}/create','ProductsController#create')->name('products.create');
this is the route that contains your client_id that we want to retrieve, and since this value is on the link, we can use the Request object to access to it!
like i said, the $request object already contains the client_id value, so what we need is to retrieve it and send it as a parameter to the view using the with function, so basically your ProductsController#create will be like this :
public function create(Request $request)
{
// all your code ...
return view ('products.create')->with("data",$datas)->with("client_id",$request->client_id);
}
so now, we access this value from our view right ? the idea is to add this value as a hidden input on the product form! ( something like this )
<form>
<!-- other inputs .... -->
<input type="hidden" value="{{$client_id}}" name="client_id" />
</form>
after submitting the form, this route will be called :
Route::post('/products/{client_id}', 'ProductsController#store')->name('products.store');
since the client_id will be sent through the form and not using the link, it would be better if you change your route to
Route::post('/products', 'ProductsController#store')->name('products.store');
now, we can have access to our value on the store function just by using
$client_id = $request->input('client_id')

Here is an alternative solution which works too:
ROUTES:
Route::get('/products/{client_id}/create', 'ProductsController#create')-name('products.create');
Route::post('/products', 'ProductsController#store')->name('products.store');
PRODUCTS CONTROLLER CREATE FUNCTION:
public function create() //keep as in the original question
{
// all your code ...
return view ('products.create')->with("data",$datas)->with("client_id",$request->client_id);
}
IN THE VIEW:
<form>
<!-- other inputs .... -->
<input type="hidden" value="{{Request::segment(2)}}" name="client_id" /> //access the URL segmment 2 which correspond to the client_id
</form>
IN THE PRODUCTS CONTROLLER STORE FUNCTION:
$client_id = $request->input('client_id')
It would have be nice to use the Request::segment(2) directly in the store function like this but this triggers a "Non-static method Illuminate\Http\Request::segment() should not be called statically" error that I have not been able to resolve. I don't need an answer to that, however an hint would be appreciated anyway. Maybe I'll post a new question if this has not been resolved already.

Related

EntityType and many to many with extra field relation presented as dropdown (select)

I created a form like that
$builder->add('employees', EntityType::class, [
'class' => ActivityEmployee::class,
'choice_label' => function (ActivityEmployee $employee) {
return sprintf('%s %s', $employee->getEmployee()->getName(), $employee->getEmployee()->getLastName());
},
'multiple' => true,
])
As a result it presents already existing data fine. It shows me all employees with relation to edited activity.
However as choices there should be all employess to choose (employee entity) and as selected data only employess in activityEmployee relation like right now.
I tried to add a query_builder option to provide lists of all employess, but I can only use EntityRepository which means ActivityEmployeesRepository not EmployeesRepository per se.
A can't figure out how to implement it. Basically such relation can be done by CollectionType of custom activityEmployeeType but I'd like to use multi-select for selecting employees.
I can use another approach to not mapping my employees field to entity like that
$currentEmployees = [];
foreach ($activity->getEmployees() as $activityEmployee) {
$currentEmployees[] = $activityEmployee->getEmployee();
}
$builder->add('employees', EntityType::class, [
'class' => Employee::class,
'choice_label' => function (Employee $employee) {
return sprintf('%s %s', $employee->getName(), $employee->getLastName());
},
'mapped' => false,
'multiple' => true,
'data' => $currentEmployees,
]);
It works fine, but I need to deal with updating relation by myself. Which is ok, however I wonder how to achieve such thing in first approach.
Implementation details matter. As far as I can understand you have the following entities:
Activity (entity)
- employees (OneToMany -> ActivityEmployee)
ActivityEmployee (entity)
- activity (ManyToOne -> Activity)
- employee (ManyToOne -> Employee)
Employee (entity)
- activities (OneToMany -> ActivityEmployee) - this one might be missing, actually.
Now you apparently don't hide any implementation details. Meaning, your Activity::getEmployees() returns []ActivityEmployee.
I would have done it like this:
class Activity {
/** #ORM\OneToMany(targetEntity=ActivityEmployee::class) */
private $activityEmployees;
/** #return Employee[] */
public function getEmployees() :Collection {
return $this->activityEmployees->map(function(ActivityEmployee $ae) {
return $ae->getEmployee();
});
}
public function addEmployee(Employee $employee) {
// check, if the employee is already registered, add only then!
if(!$this->getEmployees()->contains($employee)) {
$this->activityEmployees->add(new ActivityEmployee($this, $employee));
}
}
public function removeEmployee(Employee $employee) {
foreach($this->activityEmployees as $activityEmployee) {
if($activityEmployee->getEmployee() === $employee) {
$this->activityEmployees->removeElement($activityEmployee);
}
}
}
}
This way, you hide away how Activity handles the employees and to the outside world (and specifically the PropertyAccessor, that the form component uses) it appears as if Activity has a property employees which are actually Employee[].
If you implement it like this, your first form should actually just work (obviously exchanging ActivityEmployee for Employee) - under the assumption that I didn't make some major mistake. Of course I would also add methods like getActivityEmployees when I would actually specificially need the relation objects.
This whole thing certainly is less beautiful if your many-to-many can contain duplicates.
IF your ActivityEmployee actually has NO other properties besides activity and employee, you could obviously replace the whole thing with a #ORM\ManyToMany and just work with Employee[] instead of the ActivityEmployee[]. However, I assume you have some additional columns like created or something.

get message related to InlineKeyboardButton telegram bot after touch it

I have a command to get some suggestion/criticism in some specific categories that are shown as some InlineKeyboardButton. first user touch one of them and then should be send a simple text as suggestion/criticism.
this is my command:
class SuggestionCommand extends SystemCommand
{
protected $name = 'suggestion'; // Your command's name
protected $description = 'Suggestion Command'; // Your command description
protected $usage = '/suggestion'; // Usage of your command
protected $version = '1.0.0'; // Version of your command
public function execute()
{
$inlineKeyboard = new InlineKeyboard([]);
$inlineKeyboard->addRow(new InlineKeyboardButton([
'text' => 'Sellers',
'callback_data' => 'suggestion_sellers'
]), new InlineKeyboardButton([
'text' => 'Products',
'callback_data' => 'suggestion_products'
]));
$chat_id = $this->getMessage()->getChat()->getId();
$data = [
'chat_id' => $chat_id,
'text' => 'In which of the following areas is your suggestion / criticism: ',
'reply_markup' => $inlineKeyboard
];
return Request::sendMessage($data);
}
}
What I want to do is do that is when user enter his text I want to recognize which button is touched in The previous step because I want to do other actions based on that button.
is there any way to do that?
You need to get the callback_data value and store it in a database or JSON file. Then store the upcoming message as a reply to this value.

SQLSTATE[23000]: Integrity constraint violation in laravel5.8.35

I am very new to laravel and sqlite and am trying to create a new 'dish' to my list of dishes, however, once I submit the creation form I receive the following error:
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL
constraint failed: dishes.user_id (SQL: insert into "dishes" ("name",
"price", "user_id", "updated_at", "created_at") values (Supreme Pizza,
15, ?, 2019-09-29 05:43:42, 2019-09-29 05:43:42))
I have tried a number of different modifications in my web.php, DishController, UserController, and the create.blade.php form itself, but none of the changes have worked. There were some similar questions asked here but none of the solutions helped my particular problem either.
This is my web.php file
Route::resource('dish', 'DishController');
Route::resource('user', 'UserController');
Auth::routes();
Route::get('/', 'UserController#index');
Route::get('/show/{id}', 'UserController#show');
Route::get('/show/{id}', 'UserController#create');
Route::get('users/{user}', ['as' => 'users.edit', 'uses' => 'UserController#edit']);
Route::patch('users/{user}/update', ['as' => 'users.update', 'uses' => 'UserController#update']);
Route::get('/dish', 'DishController#index');
Route::get('/show/{id}', 'DishController#show');
Route::get('/dish/create', ['as' => 'dish.create', 'uses' => 'DishController#create']);
Route::get('dishes/{dish}', ['as' => 'dish.edit', 'uses' => 'DishController#edit']);
Route::patch('dishes/{dish}/update', ['as' => 'dishes.update', 'uses' => 'DishController#update']);
Route::get('/dish/{dish}', 'DishController#destroy');
The relevant part of my DishController:
public function index()
{
$dishes = Dish::all();
return view('dishes.index')->with('dishes', $dishes);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return View::make('dishes.create_form')->with('users', User::all());
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
'price' => 'required|integer|min:1',
'user' => 'exists:user,id'
]);
$dish = new Dish();
$dish->name = $request->name;
$dish->price = $request->price;
$dish->user_id = $request->user;
$dish->save();
return redirect("dish/$dish->id");
}
And the relevant part of my create_form:
<form method="POST" action='{{url("dish")}}'>
{{csrf_field()}}
<p><label>Name: </label><input type="text" name="name" value="{{old('name')}}"></p>
<p><label>Price: </label><input type="text" name="price" value="{{old('price')}}"></p>
<p><select name="Restaurant">
#foreach ($users as $user)
#if ($user->role == 'restaurant')
<option value="{{$user->id}}" selected="selected">{{$user->name}}</option>
#endif
#endforeach
</select></p>
<input type="submit" value="Create">
</form>
The general idea is that I list my users that have a 'role' of 'restaurant'
and when I click on one, it takes me to the dishes they have (i.e. lists all dishes with selected user_id), and the option to create a new dish.
When I try to create a new dish, I input all necessary data and submit the form but I get the error message as seen in the Title. It says that dishes.user_id is null, and shows it as '?'.
This is odd since the POST data shows that:
name = "Supreme Pizza"
price = "15"
Restaurant = "7" (which is correct)
I can't for the life of me figure out what the problem is, so any help would be greatly appreciated.
In your PHP controller you are using $request->user. which i don't see in your HTML Form. So, If you want to add current user. You can use Auth.
public function store(Request $request)
{
$this->validate([
'name' => 'required|max:255',
'price' => 'required|integer|min:1',
'user' => 'exists:user,id'
]);
$dish = new Dish();
$dish->name = $request->name;
$dish->price = $request->price;
$dish->user_id = Auth::user()->id;
$dish->save();
return redirect("dish/$dish->id");
}
Please Also use Auth in top of Controller
use Auth;

How do you Filter CSV exports on silverstripe CMS?

I am trying to export a CSV of my data which is currently displayed in a section of my Silverstripe CMS as filtered by a particular date range . It works fine at the moment when exporting the entire contents but I would like to be able to filter the results that are exported so that it returns all results within a particular date range.
My Database has a column thats records the date created - in the format 'D-M-Y; H-M-S' which I think could be used to do the filtering but I cant figure out how to set up the search filter. I understand that if you use the searchable fields and then export, you only export the filtered search results so would assume thats the best way of doing it but can't figure out how to implement it.
Any suggestions would be greatly appreciated.
-- disclaimer - I would have liked to put this on the silverstripe forum but I am completely unable to sign up for some reason - I never receive the email confirmations. ---
<?php
namespace AffiliateProgram;
use SilverStripe\Forms\GridField\GridField;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
use SilverStripe\Security\Permission;
use SilverStripe\ORM\DataObject;
class MemberBonus extends DataObject
{
private static $db = [
'Amount' => 'Currency',
'Confirmed' => 'Boolean',
'Level' => 'Int',
'Percentage' => 'Int'
];
private static $has_one = [
'Member' => 'AffiliateProgram\Member',
'MemberPayment' => 'AffiliateProgram\MemberPayment',
'PaymentType' => 'AffiliateProgram\PaymentType',
'ProgramType' => 'AffiliateProgram\ProgramType'
];
private static $summary_fields = [
'Amount' => 'Amount (USD)',
'Member.Email' => 'Email',
'Level',
'MemberPayment.PaymentType.Symbol' => 'Recieved As',
'Percentage' => 'Percentage Bonus Applied',
'ProgramType.Name' => 'Program Type',
'MemberPayment.Created' => 'Payment Date',
'Confirmed' => 'Confirmed?',
'MemberPayment.ID' => 'Payment ID'
];
}
There is also a DateCreated column on the table.
You can add custom search fields to a ModelAdmin via getSearchContext(), and customise the query based on them with getList(). See this section of the SilverStripe documentation.
Here's an example of excluding results that have a CreatedAt value below the date provided in a search field (assuming your ModelAdmin only manages MemberBonus):
<?php
use SilverStripe\Admin\ModelAdmin;
use SilverStripe\Forms\DatetimeField;
class MemberBonusAdmin extends ModelAdmin
{
...
public function getSearchContext()
{
$context = parent::getSearchContext();
$context->getFields()->push(new DatetimeField('q[CreatedAfter]', 'Created After'));
return $context;
}
public function getList()
{
$list = parent::getList();
$params = $this->getRequest()->requestVar('q');
if (!empty($params['CreatedAfter'])) {
$list = $list->exclude('CreatedAt:LessThan', $params['CreatedAfter']);
}
return $list;
}
}
To get a range working, you'd just need to add a CreatedBefore field and filter.

Use VBO in Drupal to update custom user field

I want to perform bulk update of users with a Approved users, the table
field_user_status_value
-----------------------
entity_type, entity_id, field_user_status_value
The entity_id is the user id which does not exist in the table, below is the custom module I wrote to update the table:
function bulkapprove_action_info() {
return array(
'bulkapprove_action_callback_name' => array(
'type' => 'user', // Can be file, term, user, etc.
'label' => t('Approve User'),
'configurable' => FALSE, // Doesn't need config form
'behavior' => array('view_property'), // Uses view access rights ,
'pass rows' => TRUE,
'triggers' => array('any'), // Works always
),
);
}
function bulkapprove_action_callback_name($entity, $context)
{
db_update('field_data_field_user_status')->fields(array('field_user_status_value' => 'Approved'))->condition('entity_id', $context->entity_id)->execute();
}
But it is not inserting the values in this table
In Drupal you do not want to update the database fields directly unless you created the table. Drupal's internal APIs provide a collection of tools to ensure you update the values correctly and that all supporting modules get notified of changes as needed through the hook system.
In this case the callback gets the actual entity to run your action against (in this case the user object). You want to take action on that entity and then save the entity.
function bulkapprove_action_callback_name($entity, $context)
{
$entity->status = 1;
entity_save('user', $entity);
}

Resources