I'm trying to CRUD with livewire and I'm having some troubles.
Here you can see my main controller component.
class Topics extends Component
{
public function index()
{
$topics = Topic::all();
return view('topics.index', ['topics'=>$topics]);
}
public function create()
{
return view('topics.create');
} /* The store function is in a single component as I say after */
public function show(Topic $topic)
{
return view('topics.show', compact('topic'));
}
public function edit(Topic $topic)
{
return view('topics.edit', compact('topic'));
}
public function destroy(Topic $topic)
{
//
}
public function render()
{
return view('livewire.topics');
}
}
At the beginning I would try to regroup all the CRUD functions in one file livewire. But it was impossible for me to use the store function. I needed to create a component only for storing.
Maybe you have a solution for that too? But it's not my main problem here.
As the store function, the update function doesn't work, so I created a component name "edit".
Here is the problem.
I can :
see all my topics in my (topics.index)
see my create form in my livewire component named "store" include on the view (topics.create) and create a topic
see only one topic with the show function on (topics.show)
click on my "edit" button et see my page "edit" in (topics.edit) but when I include my livewire component named "edit" which contains my update form, I can't for example doing this: "$topic->title"
I can't give to this component the information of the topic I clicked on.
$topic variable is unsigned.
How, in your opinion, can I give to this component the information of the topic I want to edit?
I put here the rest of my actual view and controller.
The store component:
class Store extends Component
{
protected $rules = [
'title' => 'required',
'content' => 'required',
];
public $title;
public $content;
public $user_id;
public function store()
{
$this->user_id = auth()->user()->id;
$this->validate();
$topic = Topic::create([
'title' => $this->title,
'content' => $this->content,
'user_id' => $this->user_id,
]);
return redirect()->route('topics.show', $topic->id);
}
public function render()
{
return view('livewire.test');
}
}
The edit controller's component:
class Edit extends Topics
{
public function render()
{
return view('topics.edit');
}
}
As you can see I tried to extend the Topics controller to pass the $topic... but it doesn't work of course.
I tried:
<div>
<livewire:edit :topic="$topic">
</div>
And some other thing.
Thank you for helping me
You need to define the mount function in your Livewire:
class Store extends Component
{
protected $rules = [ ... ];
public $topic = NULL;
public function mount($topic) {
$this->topic = $topic
}
In Livewire components, you use mount() instead of a class constructor __construct() like you may be used to. NB: mount() is only ever called when the component is first mounted and will not be called again even when the component is refreshed or rerendered.
In addition in your case, if you are separating the View model like that you might as well consider to have the Livewire initiate separately. so in Store also add a $listener and then use javascript to load the Livewire...but that is a whole another question and another answer.
Related
Is it possible to access more than one IViewComponentResult method in a single ViewComponent Class? I seem to be able to create more then one method with each referencing their own View, but I have been unable to find a way to reference the individual methods within the class from a razor view.
public class PageHeaderViewComponent: ViewComponent
{
public IViewComponentResult Header(LayoutModel layoutModel)
{
return View("Header", layoutModel);
}
public IViewComponentResult PageHeaderUpper(LayoutModel layoutModel)
{
return View("PageHeaderUpper", layoutModel);
}
}
I suggest you can pass a parameter to Invoke method to conditionally return different view:
public class PageHeaderViewComponent : ViewComponent
{
public IViewComponentResult Invoke(LayoutModel layoutModel,string name)
{
if(name=="header")
{
return View("Header", layoutModel);
}
else
{
return View("PageHeaderUpper", layoutModel);
}
}
}
You can invoke the view components like below in main view:
#await Component.InvokeAsync(typeof(PageHeaderViewComponent), new { LayoutModel= new LayoutModel() , name = "header" })
#await Component.InvokeAsync(typeof(PageHeaderViewComponent), new { LayoutModel= new LayoutModel() , name = "aaa" })
View Components location:
Besides, you can also use the property in LayoutModel as the condition.
My normal web app runs w/o any issue. Then I wanted to experiment with APIs. I enabled Passport since I need api authorization (but at this moment, I rather want to get this thing working and I have no idea whether it is a problem with Passport) and I wanted to get simple json output of specific Product. So far, I was not able to get it working. I'll describe contents of each file and if someone can direct me to find the issue in my code, that would be great.
Resources\Product.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Product extends JsonResource
{
public function toArray($request)
{
return parent::toArray($request);
}
}
Providers\AuthServiceProviders.php
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
User.php
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
...
}
ProductController.php
class ProductController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function apiShow(Product $product)
{
return new ProductResource($product);
}
...
}
routes/api.php
Route::get('/products/{product}', 'ProductController#apiShow');
Now if I go to http://localhost/public/products/1, it displays the page as expected. But if I type in http://localhost/public/api/products/1, it will always go to home page which is set to localhost/public in HomeController.
If I modify routes/api.php as:
Route::get('/products/{id}', function($id) {
return Product::find($id);
});
I get the correct json output in the browser.
Let's say that I had the following API set up:
Controller:
<?php
namespace app\modules\v1\controllers;
use yii;
class ResourceController extends \yii\rest\ActiveController
{
public $modelClass = 'app\modules\v1\models\Resource';
}
Model:
use yii;
class Resource extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'ResourceTable';
}
public function fields()
{
return [
'id' => 'ResourceID',
'title' => 'ResourceTitle',
];
}
}
where my table only has the two columns, ResourceID and Title.
When I try a GET request on the API, it works fine and returns the list of resources (or single resource in the case of resource/{id}) with the aliased field names. But when I try to POST to create a resource, I want to use the aliased field names (e.g. title instead of ResourceTitle). The problem is that the default CreateAction supplied by Yii does $model->load(), which looks for the field names in the table. If I use the aliased names then it returns an error. If I use the table field names, it works fine.
So my question is, is there a way to expose resource attributes to the end user where the field names (using the fields() function) are the same for reading and creating? If possible, I'd like to avoid writing my own CreateAction.
It's necessary to add rules for new virtual properties, if you want to $model-load() save parameters to them
class OrganizationBranch extends BaseOrganization{
public function rules()
{
return array_replace_recursive(parent::rules(),
[
[['organizationId', 'cityId'], 'safe'],
]);
}
public function fields() {
return ['id',
'cityId' => 'city_id',
'organizationId' => 'organization_id',
'address',
'phoneNumbers' => 'phone_numbers',
'schedule',
'latitude',
'longitude',
];
}
public function extraFields() {
return ['branchType', 'city'];
}
public function getOrganizationId() {
return $this->organization_id;
}
public function setOrganizationId($val) {
$this->organization_id = $val;
}
public function getCityId() {
return $this->city_id;
}
public function setCityId($val) {
$this->city_id = $val;
}
}
You can create getters/setters for alias.
public function getTitle(){ return $this->ResourceTitle; }
public function setTitle($val){ $this->ResourceTitle = $val ; }
Trying to figure out why something like the below cant work. There is a tab in the preferences admin section that points to this controller but when going to it it always output a blank page. I have tried several things but the below is basically what I need. There is no MODEL... I simply need this to get the uploaded file for postProcessing...
The controller file...
class AdminAstroImporterController extends ModuleAdminController {
public function initContent() {
parent::initContent();
return $this->display(__FILE__, 'import.tpl');
}
public function postProcess() {
//do something here
}
}
Looks like you can overide the actual content output by doing as shown in the initContent() function shown below. The 'content' in the smarty assign can be any html you generate yourself.
class AstroImporterAdminController extends AdminController {
public function __construct() {
parent::__construct();
//load current settings
$this->data = unserialize(Configuration::get('ASTRO_IMPORTER'));
}
public function initContent() {
parent::initContent();
$this->show_toolbar = false;
$this->context->smarty->assign(array(
'content' => $this->renderSettings().$this->renderForm().$this->displayFields(),
));
}
I found that this works:
public function renderList()
{
global $currentIndex, $cookie;
$smarty = $this->context->smarty;
$smarty->assign('currentIndex', $currentIndex);
return $this->context->smarty->fetch($this->getTemplatePath().'/main.tpl');
}
Although its dirty as hell, it seems cleaner than Amb3rL4nn answer.
In v1.4 it was very easy to create a tab (and easy to find documentation) I wonder why they changed it and didn't supply an docs.
I am new to yii.
I am using more than 1 controller in my website and each controller has few actions.
I want to use some variables across each controller (Value of variable will be fixed, I need some constants for a formula). Whats the best place (standard way) to define those variables ?
Should I use session ? (as value is not going to change).
Not sure what you are using your vars for, but you can do it by defining them in your config main.php
'params'=>array(
'someVar1'=>'varValue1',
'someVar2' => 'varValue2',
),
Then you can access them in ANYWHERE by calling
Yii::app()->params['someVar1']
They will be available anywhere in your application.
Or you can extend all your controllers off of a base class and define your constants there
Base Controller:
class Controller extends CController {
const SOME_VAR = 'someValue';
}
Your controller:
class YourController1 extends Controller
{
public function actionIndex()
{
echo parent::SOME_VAR;
}
}
Your other controller:
class YourController2 extends Controller
{
public function actionLogin()
{
echo parent::SOME_VAR;
}
}