How to use a librarie in views in Codeigniter4 - codeigniter-4

i trie to use a own library in a view, but it failed with "undefined property: CodeIgniter\View\View::$mylib"
This is the Base Controller
protected $mylib;
/**
* Constructor.
*/
public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
{
// Do Not Edit This Line
parent::initController($request, $response, $logger);
//--------------------------------------------------------------------
// Preload any models, libraries, etc, here.
//--------------------------------------------------------------------
// E.g.:
// $this->session = \Config\Services::session();
//Include our librarie with http-request
$this->mylib = new mylib(service('request'));
}
In the Controller i use it in this way, here i can work with my libray without any trouble. The testfunction will return a simple text.
namespace App\Controllers;
//Important to add our librarie (session, rbac, login etc.)
use App\Libraries\mylib;
...
die("Test: $this->mylib->testfunction());
If i try the same in my view file i recieve the error.
die("Test: $this->mylib->testfunction());
What i do wrong?
Update
In the meantime i find a way to work with my library in the views
at top of my view-file i add this
use App\Libraries\mylib;
$this->mylib = new mylib(service('request'));
It works, but is there a way to make the whole thing easier so that I don't have to write these lines in every view-file but maybe only once in e.g. Base-Controller?

What i do wrong?
You're assuming that because you created that Library in the Controller that it is automatically accessible in a View, which is incorrect, regardless if it's a class property of the Controller or not.
Anything that is not already in the View needs to be passed in when you "use" it (i.e. the view() function's second parameter). So you can either create the Library in the Controller and pass the entire object into the View, or continue creating it directly in the View as you're doing now.
The first option tends to be a little cleaner.

Related

How to use default constructor in codeIgniter4?

I want to use controller to load directly some models by using default constructor, but CodeIgniter4 was removed __construct method from CodeIgniter4 framework and I got error message Cannot call constructor. please see code below:
public function __construct(){
parent::__construct();
//Do magic task here
}
Instead of CI3 is working by using above code, Can anyone suggest me in CI4?
Thank You for your tips or comments!
Best Regards!
CI4 default controller namespaced CodeIgniter\Controller doesn't have a class constructor. So if you're extending directly from it, parent::__construct() can't be called.
If you really need a constructor for every one of your controller you should modify App\Controllers\BaseController and making your others controllers extending it with the code you provided.
Also if your goal is to execute some code before or after your controller is called you should check out Filters in CI4. They are perfectly designed for this need : https://codeigniter4.github.io/userguide/incoming/filters.html
You can use constructor in CI4 to load models
Add the code in your Controller class and it should work.
protected $userModel;
public function __construct()
{
$this->userModel = new UserModel();//Create a instance of the model
helper('form', 'url');
}

passing a parameter from a content type to a module

I assumed this would be easy but I am stumped.
I have a custom content type that includes an id field. I render these with a basic page template.
I have written a small module that creates a block which is populated with data from an external API. Everything works except I cannot seem to figure out how to pass the value of the id from the content of a given page to my module so it can make the API call.
It would be a couple of lines of code in straight php, it can't be that complicated in Drupal 8 and twig can it?
I managed to find a solution here
I am re-posting it in case it is useful to anyone else.
If you are generating a custom block you can access content fields via the routing system inside your block build function like this:
public function build() {
if ($node = \Drupal::routeMatch()->getParameter('node')) {
$field_my_custom_value = $node->field_my_custom_value->value;
}
//do something with the variable, like make the API call
//Make sure to set the cache to the context or even to zero if you need
return array(
'#markup' => $this->t('my content to render'),
'#cache' => array(
'contexts' => ['contexts' => ['route']],
),
);
}
I think you can reach what you want with a HOOK_preprocess.
use:
YOUR_MODULE_preprocess_node(&$variables){ ... } or
YOUR_MODULE_preprocess_block(&$variables){ ... }
to access your variable from the content type and pass it to your function oder template.

constructor parameters on controller actions

I am trying to create a custom manager which is passed in the controller when it is being called and I am having troubles understanding the current implementation of new MVC5 project in c#.
Here is the default implementation:
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
above all of that are declarations for them:
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
Now from my understanding the SignInManager and UserManager get created when application gets created for the first time in Startup.Auth.cs which looks like this:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
So now whenever I call UserManager I will get that first instance that was created when project ran for the first time.
I have 2 questions. Question 1 is is anything I said above wrong and Do I have a wrong understanding of how MVC5 works?
Question2: How is UserManager and SignInManager generated and passed in the controller? Where is the code that creates that first instance of the manager and passes it in the controller? I am assuming it is app.CreatePerOwnContext that does it. If so, can I then just create my own Manager and then register it with Owin in the same fashion and reuse throughout the project? Will my code get the latest data from the database if I do this and not cache it?
The code you're showing is coming from the IMO very ugly MVC5 template, which works out of the box but does some ugly things.
This constructor:
public AccountController(ApplicationUserManager userManager,
ApplicationSignInManager signInManager)
makes you think OWIN automagically injects the managers for you. But in fact this is not the case. That is why the template comes with the ugly properties you supplied in the questions. When you do not change anything to the template, the default constructor is called (also present in the template). To try it, just delete, or comment, the default constructor and you'll see the AccountController can't be created anymore.
So what is actually happening there is that both managers are located using the Service Locator anti pattern in the getters of the supplied properties.
So now whenever I call UserManager I will get that first instance that was created when project ran for the first time?
No this is not the case. What this line:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
does, is creating a delegate to the Create method of both managers. The managers are cached within an Owin Request. The next request the delegates are called again and you get a fresh ApplicationUserManager etc.
To be a little bit more verbose this line could be rewritten as:
Func<ApplicationUserManager> userManagerFactory = () => ApplicationUserMangager.Create();
app.CreatePerOwinContext<ApplicationUserManager>(userManagerFactory);
So if you would a breakpoint here:
public ApplicationUserManager UserManager
{
get
{
// place breakpoint here
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
// ....
You would see that while stepping through the code, you will hit the line where you created the UserManagerFactory which in his turn will call the Create() method of the ApplicationUserManager.
How is UserManager and SignInManager generated and passed in the controller
It isn't! You would need to use dependency injection for that.
If so, can I then just create my own Manager and then register it with Owin in the same fashion and reuse throughout the project
Yes you can. You can completely refactor the ApplicationUserManager you also got 'for free' in the template. As long as you supply a factory method to the 'CreatePerOwinContext' extension method.
Will my code get the latest data from the database if I do this and not cache it?
The instances are cached on per request base. So each request you will get a new one, with a new DbContext etc.
I'm unsure how familiar you are with dependency injection but MVC5 is a pretty easy framework to start with it, IMO.
I once wrote a blogpost how to configure my DI container of choice (Simple Injector) to use with the MVC5 template.
I also wrote several answers here on SO regarding this template: specifically this one, should interest you. This one is interesting also!

Orchard Custom User Part is not stored in Database

I can't seem to store additional data in a separate contentpart attached to User. I have done the following:
Created a module
In the module I created a Model for ProfilePart and ProfilePartRecord
In the migration I created a table for ProfilePartRecord (from type ContentPartRecord)
In the migration I altered the typedefinition for User, by setting WithPart ProfilePart
I created a driver class, that has 2 edit methods, one for get and one for post (code snippets are below
I also created a handler that adds a storage filter for profilePartRepository of type ProfilePartRecord
Module Structure
Drivers/ProfilePartDriver.cs
Handlers/ProfileHandler.cs
Models/ProfilePart.cs
Models/ProfilePartRecord.cs
Views/EditorTemplates/Parts/profile.cshtml
Migrations.cs
Placement.info
Since I think the issue is in the Driver. This is my code:
Is it going wrong because the part is attached to User? Or am I missing something else.
public class ProfilePartDriver:ContentPartDriver
{
protected override string Prefix
{
get { return "Profile"; }
}
//GET
protected override DriverResult Editor(ProfilePart part, dynamic shapeHelper)
{
return ContentShape("Parts_Profile_Edit", () =>
shapeHelper.EditorTemplate(TemplateName: "Parts/Profile", Model: part, Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ProfilePart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
I have used Skywalker's blog. There is one chapter about registering customers by using the User and adding your own content parts to it. Worked nice for me.
First of all - is your ProfilePart editor shown at all when you go to Dashboard and edit a given user? I noticed you're using Parts_Profile_Edit as a shape key, but actually use EditorTemplates/Parts/Profile.cshtml as a template. It's perfectly correct, but note that Placement.info file uses shape keys, so you have to use Parts_Profile_Edit as a shape name in there. Otherwise it won't get displayed.
Second - have you tried debugging to see if the second driver Editor method (the one for handling POST) is being called at all?
Like Bertrand suggested, I'd look into one of the existing modules that work (afaik there is one for user profile in the Gallery) and see the difference. It might be something small, eg. a typo.

Data binding with plugins using MEF?

I have an application that has a class named: UploadItem. The application creates uploading tasks based on information it has, for example, an upload needs to be created to upload a file to sitex.com with this the application creates a new UploadItem and adds that to an ObservableCollection, the collection is bound to a listview.
Now comes the part that I cannot solve.. I decided to change the structure so that people can create their own plugins that can upload a file, the problem lies with the fact that the UploadItem class has properties such as:
string _PercentagedDone;
public string PercentageDone
{
get { return _PercentagedDone; }
set { _PercentagedDone = value + "%"; NotifyPropertyChanged("PercentageDone"); }
}
But the plugin controls on how a file is uploaded, so how would the plugin edit the PercentageDone property that is located in the UploadItem class? If there is no way to do such a thing, then is there another way to achieve the same, i.e. showing the progress on the main GUI?
You'll want to define an interface for the plugins. Something like:
public interface IUploadPlugin
{
Task<bool> Upload(IEnumerable<Stream> files);
int Progress { get; }
}
The plugins then need to implement this interface and export themselves:
[Export(typeof(IUploadPlugin))]
public class MyUploader : IUploadPlugin, INotifyPropertyChanged
{
// ...
}
Notice that this plugin implements INotifyPropertyChanged. This is an easy way to handle updating the progress. Fire PropertyChanged on the Progress property and then databind your ProgressBar control in the main view to this property. Make sure that you fire PropertyChanged on the UI thread.
Another option would be to fire a custom event when the property changes. You could handle this event in the main view logic and update the progress.
Notice that I'm using Task for the return. This allows the caller to wait until the upload task finishes. You could use a callback instead, but with the CTP of the next version of .NET, using Task<> will allow you to use the await keyword for your async programming. Check it out here and here.

Resources